From 69c335ca8c848ccdccbedec6bd14c25c3e683509 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Wed, 22 Feb 2023 13:12:56 -0800 Subject: [PATCH] update wil to 0b2d6c2d822bb301e7558a14ee66d567c14f5dc7 --- Externals/WIL/CMakeLists.txt | 48 +- Externals/WIL/README.md | 12 +- Externals/WIL/cmake/common_build_flags.cmake | 92 +- Externals/WIL/include/wil/Tracelogging.h | 3359 ++++++++++ Externals/WIL/include/wil/com.h | 241 +- .../WIL/include/wil/com_apartment_variable.h | 467 ++ Externals/WIL/include/wil/common.h | 247 +- Externals/WIL/include/wil/cppwinrt.h | 173 +- Externals/WIL/include/wil/cppwinrt_helpers.h | 352 + Externals/WIL/include/wil/cppwinrt_wrl.h | 74 + Externals/WIL/include/wil/filesystem.h | 192 +- Externals/WIL/include/wil/nt_result_macros.h | 168 + Externals/WIL/include/wil/resource.h | 1364 +++- Externals/WIL/include/wil/result.h | 41 +- Externals/WIL/include/wil/result_macros.h | 678 +- Externals/WIL/include/wil/result_originate.h | 35 +- Externals/WIL/include/wil/safecast.h | 140 +- Externals/WIL/include/wil/stl.h | 96 +- Externals/WIL/include/wil/token_helpers.h | 47 +- .../WIL/include/wil/traceloggingconfig.h | 71 + Externals/WIL/include/wil/win32_helpers.h | 489 +- .../WIL/include/wil/win32_result_macros.h | 104 + Externals/WIL/include/wil/winrt.h | 199 +- Externals/WIL/include/wil/wistd_config.h | 39 +- Externals/WIL/include/wil/wistd_functional.h | 43 +- Externals/WIL/include/wil/wistd_memory.h | 66 +- Externals/WIL/include/wil/wrl.h | 43 + Externals/WIL/natvis/wil.natvis | 114 + Externals/WIL/packaging/nuget/CMakeLists.txt | 2 + ...osoft.Windows.ImplementationLibrary.nuspec | 1 + ...soft.Windows.ImplementationLibrary.targets | 3 + Externals/WIL/scripts/azure-pipelines.yml | 12 +- Externals/WIL/scripts/build_all.cmd | 28 +- Externals/WIL/scripts/init.cmd | 47 +- Externals/WIL/scripts/init_all.cmd | 19 +- Externals/WIL/scripts/runtests.cmd | 70 +- Externals/WIL/tests/CMakeLists.txt | 83 +- .../WIL/tests/ComApartmentVariableTests.cpp | 401 ++ Externals/WIL/tests/ComTests.cpp | 105 + Externals/WIL/tests/CommonTests.cpp | 2 +- Externals/WIL/tests/CppWinRT20Tests.cpp | 2 + Externals/WIL/tests/CppWinRTTests.cpp | 300 + Externals/WIL/tests/FakeWinRTTypes.h | 12 +- Externals/WIL/tests/FileSystemTests.cpp | 172 + Externals/WIL/tests/NtResultTests.cpp | 165 + Externals/WIL/tests/ResourceTests.cpp | 129 + Externals/WIL/tests/ResultTests.cpp | 30 +- Externals/WIL/tests/Rpc.cpp | 4 +- Externals/WIL/tests/StlTests.cpp | 137 + Externals/WIL/tests/TokenHelpersTests.cpp | 6 + Externals/WIL/tests/TraceLoggingTests.cpp | 3 + Externals/WIL/tests/WatcherTests.cpp | 18 +- Externals/WIL/tests/WinRTTests.cpp | 41 +- Externals/WIL/tests/WinVerifyTrustTest.cpp | 61 + Externals/WIL/tests/app/CMakeLists.txt | 18 +- Externals/WIL/tests/catch.hpp | 5850 +++++++++++++---- Externals/WIL/tests/common.h | 12 +- Externals/WIL/tests/cpplatest/CMakeLists.txt | 37 +- Externals/WIL/tests/main.cpp | 12 + Externals/WIL/tests/noexcept/CMakeLists.txt | 26 +- Externals/WIL/tests/normal/CMakeLists.txt | 15 +- .../WIL/tests/sanitize-address/CMakeLists.txt | 49 + .../CMakeLists.txt | 25 + Externals/WIL/tests/wiTest.cpp | 254 +- Externals/WIL/tests/win7/CMakeLists.txt | 13 + .../tests/workarounds/wrl/wrl/implements.h | 125 +- 66 files changed, 14776 insertions(+), 2507 deletions(-) create mode 100644 Externals/WIL/include/wil/Tracelogging.h create mode 100644 Externals/WIL/include/wil/com_apartment_variable.h create mode 100644 Externals/WIL/include/wil/cppwinrt_helpers.h create mode 100644 Externals/WIL/include/wil/cppwinrt_wrl.h create mode 100644 Externals/WIL/include/wil/nt_result_macros.h create mode 100644 Externals/WIL/include/wil/traceloggingconfig.h create mode 100644 Externals/WIL/include/wil/win32_result_macros.h create mode 100644 Externals/WIL/natvis/wil.natvis create mode 100644 Externals/WIL/tests/ComApartmentVariableTests.cpp create mode 100644 Externals/WIL/tests/NtResultTests.cpp create mode 100644 Externals/WIL/tests/TraceLoggingTests.cpp create mode 100644 Externals/WIL/tests/WinVerifyTrustTest.cpp create mode 100644 Externals/WIL/tests/sanitize-address/CMakeLists.txt create mode 100644 Externals/WIL/tests/sanitize-undefined-behavior/CMakeLists.txt create mode 100644 Externals/WIL/tests/win7/CMakeLists.txt diff --git a/Externals/WIL/CMakeLists.txt b/Externals/WIL/CMakeLists.txt index 27765fd1c9..5adbebbfe0 100644 --- a/Externals/WIL/CMakeLists.txt +++ b/Externals/WIL/CMakeLists.txt @@ -1,13 +1,21 @@ -cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.15) project(WIL) +include(GNUInstallDirs) + # Set by build server to speed up build/reduce file/object size option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF) +option(WIL_BUILD_PACKAGING "Sets option to build the packaging, default on" ON) +option(WIL_BUILD_TESTS "Sets option to build the unit tests, default on" ON) if (NOT DEFINED WIL_BUILD_VERSION) set(WIL_BUILD_VERSION "0.0.0") endif() +if (NOT DEFINED CPPWINRT_VERSION) + set(CPPWINRT_VERSION "2.0.221121.5") +endif() + # Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise # we'll need to assume that this value comes from the command line (e.g. through the VS command prompt) if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION) @@ -17,5 +25,39 @@ else() string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}") endif() -add_subdirectory(packaging) -add_subdirectory(tests) +if (${WIL_BUILD_PACKAGING}) + add_subdirectory(packaging) +endif() + +if (${WIL_BUILD_TESTS}) + add_subdirectory(tests) +endif() + +# Gather headers into an interface library. +file(GLOB_RECURSE HEADER_FILES "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.h") +add_library(${PROJECT_NAME} INTERFACE) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +# The interface's include directory. +target_include_directories(${PROJECT_NAME} INTERFACE + $ + $ +) + +# Include the .natvis files +if (MSVC) + target_sources(${PROJECT_NAME} INTERFACE + "$") +endif() + +# Install Package Configuration +string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) +install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME_LOWER}_targets) +install(EXPORT ${PROJECT_NAME_LOWER}_targets + NAMESPACE ${PROJECT_NAME}:: + FILE ${PROJECT_NAME_LOWER}Config.cmake + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}" +) + +# Install the headers at a standard cmake location. +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/wil" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") diff --git a/Externals/WIL/README.md b/Externals/WIL/README.md index 8d4e538f81..0d47bcf97b 100644 --- a/Externals/WIL/README.md +++ b/Externals/WIL/README.md @@ -12,7 +12,8 @@ Some things that WIL includes to whet your appetite: Smart pointers and auto-releasing resource wrappers to let you manage Windows API HANDLEs, HWNDs, and other resources and resource handles with [RAII](https://en.cppreference.com/w/cpp/language/raii) semantics. -- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h): Wrappers for API functions +- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h) + ([documentation](https://github.com/microsoft/wil/wiki/Win32-helpers)): Wrappers for API functions that save you the work of manually specifying buffer sizes, calling a function twice to get the needed buffer size and then allocate and pass the right-size buffer, casting or converting between types, and so on. @@ -24,6 +25,11 @@ Some things that WIL includes to whet your appetite: Preprocessor macros to help you check for errors from Windows API functions, in many of the myriad ways those errors are reported, and surface them as error codes or C++ exceptions in your code. +- [`include/wil/Tracelogging.h`](include/wil/Tracelogging.h): This file contains the convenience macros + that enable developers define and log telemetry. These macros use + [`TraceLogging API`](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal) + to log data. This data can be viewed in tools such as + [`Windows Performance Analyzer`](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-analyzer). WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned error codes to report errors. All of WIL can be used from user-space Windows code, @@ -50,7 +56,7 @@ Note that even though WIL is a header-only library, you still need to install th # Building/Testing To get started testing WIL, first make sure that you have a recent version of [Visual Studio](https://visualstudio.microsoft.com/downloads/) and the most recent [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed. If you are doing any non-trivial work, also be sure to have a recent version of [Clang](http://releases.llvm.org/download.html) installed. Once everything is installed, open a VS -native command window (e.g. "x64 Native Tools Command Prompt for VS 2019"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory: +native command window (e.g. "x64 Native Tools Command Prompt for VS 2022"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory: ```cmd C:\wil> scripts\init.cmd -c clang -g ninja -b debug ``` @@ -76,7 +82,7 @@ C:\wil> scripts\build_all.cmd C:\wil> scripts\runtests.cmd ``` Note that this will only test for the architecture that corresponds to the command window you opened. You will want to -repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2019") +repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2022") # Contributing diff --git a/Externals/WIL/cmake/common_build_flags.cmake b/Externals/WIL/cmake/common_build_flags.cmake index c9f953fc5a..0e6839bf84 100644 --- a/Externals/WIL/cmake/common_build_flags.cmake +++ b/Externals/WIL/cmake/common_build_flags.cmake @@ -1,5 +1,6 @@ -# E.g. replace_cxx_flag("/W[0-4]", "/W4") +# This is unfortunately still needed to disable exceptions/RTTI since modern CMake still has no builtin support... +# E.g. replace_cxx_flag("/EHsc", "/EHs-c-") macro(replace_cxx_flag pattern text) foreach (flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE @@ -10,60 +11,61 @@ macro(replace_cxx_flag pattern text) endforeach() endmacro() -macro(append_cxx_flag text) - foreach (flag - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - - string(APPEND ${flag} " ${text}") - - endforeach() -endmacro() - # Fixup default compiler settings +add_compile_options( + # Be as strict as reasonably possible, since we want to support consumers using strict warning levels + /W4 /WX + ) -# Be as strict as reasonably possible, since we want to support consumers using strict warning levels -replace_cxx_flag("/W[0-4]" "/W4") -append_cxx_flag("/WX") +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options( + # Ignore some pedantic warnings enabled by '-Wextra' + -Wno-missing-field-initializers -# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl) -append_cxx_flag("/permissive-") + # Ignore some pedantic warnings enabled by '-Wpedantic' + -Wno-language-extension-token + -Wno-c++17-attribute-extensions + -Wno-gnu-zero-variadic-macro-arguments + -Wno-extra-semi -# wistd::function has padding due to alignment. This is expected -append_cxx_flag("/wd4324") + # For tests, we want to be able to test self assignment, so disable this warning + -Wno-self-assign-overloaded + -Wno-self-move -if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") - # Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed - append_cxx_flag("-Wno-switch") - append_cxx_flag("-Wno-c++17-compat-mangling") - append_cxx_flag("-Wno-missing-field-initializers") + # clang needs this to enable _InterlockedCompareExchange128 + -mcx16 - # For tests, we want to be able to test self assignment, so disable this warning - append_cxx_flag("-Wno-self-assign-overloaded") - append_cxx_flag("-Wno-self-move") + # We don't want legacy MSVC conformance + -fno-delayed-template-parsing - # clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar - # results through the following flags. - append_cxx_flag("-fno-delayed-template-parsing") + # NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That + # said, errors that originate from WIL headers may benefit + # -fno-ms-compatibility + # -ferror-limit=999 + # -fmacro-backtrace-limit=0 - # NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That - # said, errors that originate from WIL headers may benefit - # append_cxx_flag("-fno-ms-compatibility") - # append_cxx_flag("-ferror-limit=999") - # append_cxx_flag("-fmacro-backtrace-limit=0") - # -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is - # available (i.e. >= C++20) - # append_cxx_flag("-Xclang -std=c++2a") + # -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support + # is available (i.e. >= C++20) + # -Xclang -std=c++2a + ) else() - # Flags that are either ignored or unrecognized by clang-cl - # TODO: https://github.com/Microsoft/wil/issues/6 - # append_cxx_flag("/experimental:preprocessor") + add_compile_options( + # We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl) + /permissive- - # CRT headers are not yet /experimental:preprocessor clean, so work around the known issues - # append_cxx_flag("/Wv:18") + # wistd::function has padding due to alignment. This is expected + /wd4324 - append_cxx_flag("/bigobj") + # TODO: https://github.com/Microsoft/wil/issues/6 + # /experimental:preprocessor - # NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated - append_cxx_flag("/d2FH4-") + # CRT headers are not yet /experimental:preprocessor clean, so work around the known issues + # /Wv:18 + + # Some tests have a LOT of template instantiations + /bigobj + + # NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated + /d2FH4- + ) endif() diff --git a/Externals/WIL/include/wil/Tracelogging.h b/Externals/WIL/include/wil/Tracelogging.h new file mode 100644 index 0000000000..2dbfef82c6 --- /dev/null +++ b/Externals/WIL/include/wil/Tracelogging.h @@ -0,0 +1,3359 @@ +#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. +// +//********************************************************* + +#ifndef __WIL_TRACELOGGING_H_INCLUDED +#define __WIL_TRACELOGGING_H_INCLUDED + +#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<>). +#define RESOURCE_SUPPRESS_STL +#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 +#include + +#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 + +#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) + +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 + { + if (wil::init_once_initialized(m_initOnce)) + { + reinterpret_cast(m_storage)->~T(); + } + } + + T* get(void(__cdecl *cleanupFunc)(void)) WI_NOEXCEPT + { + wil::init_once_failfast(m_initOnce, [=]() -> HRESULT + { + ::new (m_storage) T(); + atexit(cleanupFunc); + reinterpret_cast(m_storage)->Create(); + return S_OK; + }); + + return reinterpret_cast(m_storage); + } + + private: + INIT_ONCE m_initOnce = INIT_ONCE_STATIC_INIT; + alignas(T) BYTE m_storage[sizeof(T)]; + }; + + // 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() + { + ::ZeroMemory(this, sizeof(*this)); + } + + StoredCallContextInfo(StoredCallContextInfo &&other) : + StoredCallContextInfo() + { + operator=(wistd::move(other)); + } + + StoredCallContextInfo& operator=(StoredCallContextInfo &&other) + { + 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) : + 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) : + m_ownsMessage(false) + { + contextId = 0; + contextName = staticContextName; + contextMessage = nullptr; + } + + StoredCallContextInfo(PCSTR staticContextName, _Printf_format_string_ PCSTR formatString, va_list argList) : + 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: + TraceLoggingHProvider Provider_() const WI_NOEXCEPT + { + return m_providerHandle; + } + + protected: + TraceLoggingProvider() WI_NOEXCEPT {} + + virtual ~TraceLoggingProvider() WI_NOEXCEPT + { + if (m_ownsProviderHandle) + { + TraceLoggingUnregister(m_providerHandle); + } + } + + 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. + */ + 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. + */ + TraceLoggingHProvider Provider() const + { + return TraceLoggingType::Provider(); + } + }; + +#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)) \ + + + // 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. + + 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(); + } + + GUID const* Id() const WI_NOEXCEPT + { + return m_pActivityData->Id(); + } + + 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); + } + + 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(); + } + + inline HRESULT GetResult() const WI_NOEXCEPT + { + return m_pActivityData->GetResult(); + } + + 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 ActivityTraceLoggingType::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 ActivityTraceLoggingType::Provider(); + } + + 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++; + } + + FailureInfo const *GetFailureInfo() const WI_NOEXCEPT + { + return (FAILED(m_result) && (m_result == m_failure.GetFailureInfo().hr)) ? &m_failure.GetFailureInfo() : nullptr; + } + + 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. + +#define __WI_TraceLoggingWriteStart(activity, name, ...) \ + __pragma(warning(push)) __pragma(warning(disable:4127)) \ + do { \ + _tlgActivityDecl(activity) \ + static const UINT64 _tlgActivity_Keyword = _tlgActivityRef(activity).Keyword;\ + static const UINT8 _tlgActivity_Level = _tlgActivityRef(activity).Level;\ + static const UINT64 _tlgActivityPrivacyTag = _tlgActivityRef(activity).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) \ + static const UINT64 _tlgActivity_Keyword = _tlgActivityRef(activity).Keyword;\ + static const UINT8 _tlgActivity_Level = _tlgActivityRef(activity).Level;\ + static const UINT64 _tlgActivityPrivacyTag = _tlgActivityRef(activity).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; } \ + 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_ void **, PCSTR contextName, _In_opt_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) : \ + ActivityBase(contextName) \ + { GetCallContext()->SetMessage(formatString, argList); StartActivity(); } \ + ActivityClassName(_In_ 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() \ + }; + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define _TLGWRITE_GENERIC_PARTB_FIELDS _GENERIC_PARTB_FIELDS_ENABLED, +#endif + +#define DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, ...) \ + void EventId() \ + { \ + __WI_TraceLoggingWriteTagged(*this, #EventId, _TLGWRITE_GENERIC_PARTB_FIELDS __VA_ARGS__); \ + } + +#define DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, ...) \ + void EventId(PCSTR correlationVector) \ + { __WI_TraceLoggingWriteTagged(*this, #EventId, _TLGWRITE_GENERIC_PARTB_FIELDS TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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. + +#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: + +#define DEFINE_TRACELOGGING_EVENT(EventId, ...) \ + static void EventId() { \ + TraceLoggingWrite(TraceLoggingType::Provider(), #EventId, \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#define DEFINE_TRACELOGGING_EVENT_CV(EventId, ...) \ + static void EventId(PCSTR correlationVector) \ + { \ + TraceLoggingWrite(TraceLoggingType::Provider(), #EventId, \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + TraceLoggingString(correlationVector, "__TlgCV__"), __VA_ARGS__); \ + } + +#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)), \ + _TLGWRITE_GENERIC_PARTB_FIELDS \ + __VA_ARGS__); \ + } + +#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. + +#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) + +#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. + +#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) + +#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. + +#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) + +#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 +#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) +#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 +#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) +#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 +#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) +#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 +#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) +#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 +#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) +#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 +#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() +#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() + +#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() +#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() + +#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() +#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. + +#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) + +#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. + +#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) + +#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. + +#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) + +#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(const ActivityClassName &) = default; \ + ActivityClassName(ActivityClassName &&) = default; \ + 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 + +// [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 + +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 delimeter + if (node->specialization) + { + totalSpecializationsLength += strlen(node->specialization) + 1; // We add 1 to account for the comma delimeter + } + else + { + totalSpecializationsLength += 2; // '-' plus comma delimeter + } + 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( + [](PTP_CALLBACK_INSTANCE, PVOID, PTP_TIMER) + { + FireEvent(); + }, + nullptr, + nullptr)); + ScheduleFireEventCallback(); + } + + ~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. + +#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) + +#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/Externals/WIL/include/wil/com.h b/Externals/WIL/include/wil/com.h index 6a994590d7..79dff91a6f 100644 --- a/Externals/WIL/include/wil/com.h +++ b/Externals/WIL/include/wil/com.h @@ -16,6 +16,13 @@ #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 @@ -46,10 +53,10 @@ namespace wil { }; - typedef wistd::integral_constant tag_com_query; - typedef wistd::integral_constant tag_try_com_query; - typedef wistd::integral_constant tag_com_copy; - typedef wistd::integral_constant tag_try_com_copy; + 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 { @@ -87,7 +94,7 @@ namespace wil template struct query_policy_helper { - typedef default_query_policy type; + using type = default_query_policy; }; class weak_query_policy @@ -99,7 +106,7 @@ namespace wil *result = nullptr; IInspectable* temp; - HRESULT hr = ptr->Resolve(__uuidof(IInspectable), reinterpret_cast(&temp)); + HRESULT hr = ptr->Resolve(__uuidof(IInspectable), &temp); if (SUCCEEDED(hr)) { if (temp == nullptr) @@ -144,7 +151,7 @@ namespace wil template <> struct query_policy_helper { - typedef weak_query_policy type; + using type = weak_query_policy; }; #if (NTDDI_VERSION >= NTDDI_WINBLUE) @@ -170,7 +177,7 @@ namespace wil template <> struct query_policy_helper { - typedef agile_query_policy type; + using type = agile_query_policy; }; #endif @@ -190,15 +197,15 @@ namespace wil class com_ptr_t { private: - typedef typename wistd::add_lvalue_reference::type element_type_reference; - typedef details::query_policy_t query_policy; + 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). - typedef typename err_policy::result result; + using result = typename err_policy::result; //! The template type `T` being held by the com_ptr_t. - typedef T element_type; + using element_type = T; //! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns). - typedef T* pointer; + using pointer = T*; //! @name Constructors //! @{ @@ -359,8 +366,7 @@ namespace wil m_ptr = other; if (ptr) { - ULONG ref; - ref = ptr->Release(); + ULONG ref = ptr->Release(); WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer"); } } @@ -397,9 +403,9 @@ namespace wil //! 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 + ::IUnknown** put_unknown() WI_NOEXCEPT { - return reinterpret_cast(put()); + return reinterpret_cast<::IUnknown**>(put()); } //! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address). @@ -863,7 +869,7 @@ namespace wil // Internal Helpers /// @cond template - inline com_ptr_t(_In_ U* ptr, details::tag_com_query) + 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)); } @@ -875,14 +881,12 @@ namespace wil } template - inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy) + 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)); - return; } - m_ptr = nullptr; } template @@ -1182,6 +1186,43 @@ namespace wil #endif /// @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 @@ -1255,7 +1296,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::query_policy_t::query(raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -1297,7 +1338,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::query_policy_t::query(raw, riid, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } //! @} @@ -1687,7 +1728,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -1802,7 +1843,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::GetWeakReference(raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -1873,14 +1914,14 @@ namespace wil /** 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) + 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) + wil::com_ptr_failfast CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT { return CoCreateInstanceFailFast(__uuidof(Class), dwClsContext); } @@ -1888,7 +1929,7 @@ namespace wil /** 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) + wil::com_ptr_nothrow CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT { return CoCreateInstance(rclsid, dwClsContext); } @@ -1896,7 +1937,7 @@ namespace wil /** 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) + wil::com_ptr_nothrow CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT { return CoCreateInstanceNoThrow(__uuidof(Class), dwClsContext); } @@ -1949,6 +1990,140 @@ namespace wil { return CoGetClassObjectNoThrow(__uuidof(Class), dwClsContext); } + +#if __cpp_lib_apply && __has_include() + 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)}; + } + } + +#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 @@ -2262,8 +2437,8 @@ namespace wil @param value Set to point to the allocated result of reading a string from `source` */ inline HRESULT stream_read_string_nothrow( - _In_ ISequentialStream* source, - _When_(options == empty_string_options::returns_empty, _Outptr_result_z_) _When_(options == empty_string_options::returns_null, _Outptr_result_maybenull_z_) wchar_t** value, + _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; @@ -2601,8 +2776,8 @@ namespace wil wil::stream_write_string(target, L"Waffles", 3); ~~~~ @param target The stream to which to write a string - @param source The string to write. Can be null if `writeLength` is zero - @param writeLength The number of characters to write from source into `target` + @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) { diff --git a/Externals/WIL/include/wil/com_apartment_variable.h b/Externals/WIL/include/wil/com_apartment_variable.h new file mode 100644 index 0000000000..3ae0b9238c --- /dev/null +++ b/Externals/WIL/include/wil/com_apartment_variable.h @@ -0,0 +1,467 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_COM_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; + }); + } + + 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; + + 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< + std::unordered_map> + 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() + { + if (auto any = get_if()) + { + return *any; + } + THROW_HR(E_NOT_SET); + } + + 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; + } + }; + } + + // 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; + }; +} + +#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED diff --git a/Externals/WIL/include/wil/common.h b/Externals/WIL/include/wil/common.h index 88dcfeadcd..5b336884a5 100644 --- a/Externals/WIL/include/wil/common.h +++ b/Externals/WIL/include/wil/common.h @@ -75,27 +75,7 @@ #define __WI_SUPPRESS_NOEXCEPT_ANALYSIS #endif -#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 -#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 // 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 @@ -107,73 +87,6 @@ WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0") #define __declspec_selectany_ __declspec(selectany) /// @endcond -#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS) -/** This define is automatically set when exceptions are enabled within wil. -It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in -_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil -header. All exception-based WIL methods and classes are included behind: -~~~~ -#ifdef WIL_ENABLE_EXCEPTIONS -// code -#endif -~~~~ -This enables exception-free code to directly include WIL headers without worrying about exception-based -routines suddenly becoming available. */ -#define WIL_ENABLE_EXCEPTIONS -#endif -/// @endcond - -/// @cond -#if defined(WIL_EXCEPTION_MODE) -static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode"); -#elif !defined(WIL_LOCK_EXCEPTION_MODE) -#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together -#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0") -#elif defined(WIL_ENABLE_EXCEPTIONS) -#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled -#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1") -#else -#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions -#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2") -#endif - -#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS) -#error Must enable exceptions when WIL_EXCEPTION_MODE == 1 -#endif - -// block for documentation only -#if defined(WIL_DOXYGEN) -/** This define can be explicitly set to disable exception usage within wil. -Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking -at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based -classes and methods from WIL, define this macro ahead of including the first WIL header. */ -#define WIL_SUPPRESS_EXCEPTIONS - -/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS. -Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to -do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations -when linking libraries together with different exception handling semantics. */ -#define WIL_LOCK_EXCEPTION_MODE - -/** This define explicit sets the exception mode for the process to control optimizations. -Three exception modes are available: -0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that - use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR - violations when linking libraries together with different exception handling semantics. -1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled. -2) This locks the binary to libraries built without exceptions. */ -#define WIL_EXCEPTION_MODE -#endif - -#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703) -#define WIL_HAS_CXX_17 1 -#else -#define WIL_HAS_CXX_17 0 -#endif - -// Until we'll have C++17 enabled in our code base, we're falling back to SAL -#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE - //! @defgroup macrobuilding Macro Composition //! The following macros are building blocks primarily intended for authoring other macros. //! @{ @@ -327,8 +240,96 @@ Three exception modes are available: //! @} // Macro composition helpers -#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 -#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 +#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 +/// @endcond + +/// @cond +#if defined(WIL_EXCEPTION_MODE) +static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode"); +#elif !defined(WIL_LOCK_EXCEPTION_MODE) +#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0") +#elif defined(WIL_ENABLE_EXCEPTIONS) +#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1") +#else +#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2") +#endif + +#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS) +#error Must enable exceptions when WIL_EXCEPTION_MODE == 1 +#endif + +// block for documentation only +#if defined(WIL_DOXYGEN) +/** This define can be explicitly set to disable exception usage within wil. +Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking +at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based +classes and methods from WIL, define this macro ahead of including the first WIL header. */ +#define WIL_SUPPRESS_EXCEPTIONS + +/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS. +Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to +do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations +when linking libraries together with different exception handling semantics. */ +#define WIL_LOCK_EXCEPTION_MODE + +/** This define explicit sets the exception mode for the process to control optimizations. +Three exception modes are available: +0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that + use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR + violations when linking libraries together with different exception handling semantics. +1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled. +2) This locks the binary to libraries built without exceptions. */ +#define WIL_EXCEPTION_MODE +#endif + +#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703) +#define WIL_HAS_CXX_17 1 +#else +#define WIL_HAS_CXX_17 0 +#endif + +// Until we'll have C++17 enabled in our code base, we're falling back to SAL +#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE + +#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t::value, void*> = nullptr +#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t::value, void*> = nullptr //! @defgroup bitwise Bitwise Inspection and Manipulation //! Bitwise helpers to improve readability and reduce the error rate of bitwise operations. @@ -612,10 +613,10 @@ namespace wil } template <> - _Post_satisfies_(return == !!val) + _Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool(unsigned char val) { - return !!val; + return (val != 0); } /** Verify that `val` is a Win32 BOOL value. @@ -651,16 +652,62 @@ namespace wil ~~~~ RETURN_HR_IF(static_cast(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId)); ~~~~ - @param val The HRESULT returning expression + @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) { - // Note: Written in terms of 'int' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT + // Note: Written in terms of 'long' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT static_assert(wistd::is_same::value, "Wrong Type: HRESULT expected"); return hr; } + + /** Verify that `status` is an NTSTATUS value. + Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the + underlying typedef behind NTSTATUS. + //! + Note that occasionally you might run into an NTSTATUS which is directly defined with a #define, such as: + ~~~~ + #define STATUS_NOT_SUPPORTED 0x1 + ~~~~ + Though this looks like an `NTSTATUS`, this is actually an `unsigned long` (the hex specification forces this). When + these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change + their definition to match the manner in which `NTSTATUS` constants are defined in ntstatus.h: + ~~~~ + #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) + ~~~~ + When these are encountered in the public SDK, their type should not be changed and you should use a static_cast + to use this value in a macro that utilizes `verify_ntstatus`, for example: + ~~~~ + NT_RETURN_IF_FALSE(static_cast(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0)); + ~~~~ + @param status The NTSTATUS returning expression + @return An NTSTATUS representing the evaluation of `val`. */ + template + _Post_satisfies_(return == status) + inline long verify_ntstatus(T status) + { + // Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS + static_assert(wistd::is_same::value, "Wrong Type: NTSTATUS expected"); + return status; + } + + /** 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) + { + // 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 @@ -706,31 +753,31 @@ namespace wil template <> struct variable_size<1> { - typedef unsigned char type; + using type = unsigned char; }; template <> struct variable_size<2> { - typedef unsigned short type; + using type = unsigned short; }; template <> struct variable_size<4> { - typedef unsigned long type; + using type = unsigned long; }; template <> struct variable_size<8> { - typedef unsigned long long type; + using type = unsigned long long; }; template struct variable_size_mapping { - typedef typename variable_size::type type; + using type = typename variable_size::type; }; } // details /// @endcond @@ -739,6 +786,10 @@ namespace wil 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); } // wil #pragma warning(pop) diff --git a/Externals/WIL/include/wil/cppwinrt.h b/Externals/WIL/include/wil/cppwinrt.h index c7da612fec..1cc8d263ba 100644 --- a/Externals/WIL/include/wil/cppwinrt.h +++ b/Externals/WIL/include/wil/cppwinrt.h @@ -14,6 +14,7 @@ #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 @@ -27,18 +28,43 @@ namespace wil::details { // Since the C++/WinRT version macro is a string... - inline constexpr int major_version_from_string(const char* versionString) + // For example: "2.0.221104.6" + inline constexpr int version_from_string(const char* versionString) { int result = 0; - auto str = versionString; - while ((*str >= '0') && (*str <= '9')) + while ((*versionString >= '0') && (*versionString <= '9')) { - result = result * 10 + (*str - '0'); - ++str; + 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; + } } /// @endcond @@ -74,6 +100,9 @@ static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, // use it unless the version of C++/WinRT is high enough extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; +// The same is true with this function pointer as well, except that the version must be 2.X or higher. +extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept; + /// @cond namespace wil::details { @@ -108,7 +137,7 @@ namespace wil::details catch (const winrt::hresult_error& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); - return exception.code().value; + return exception.to_abi(); } catch (const std::bad_alloc& exception) { @@ -149,7 +178,7 @@ namespace wil::details catch (const winrt::hresult_error& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); - return exception.code().value; + return exception.to_abi(); } catch (const std::bad_alloc& exception) { @@ -192,6 +221,12 @@ namespace wil 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; @@ -199,9 +234,15 @@ namespace wil { 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 { @@ -236,6 +277,11 @@ namespace wil 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 { @@ -246,6 +292,117 @@ namespace wil { 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: + ~~~~ + 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(); + ~~~~ + + Or it can be used as a member (with [[no_unique_address]] to avoid + occupying any memory): + ~~~~ + 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(); + ~~~~ + + 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. + ~~~~ + 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 + ~~~~ + + 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.) + ~~~~ + 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(); + }; + ~~~~ + */ + 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(); + } + }; } #endif // __WIL_CPPWINRT_INCLUDED diff --git a/Externals/WIL/include/wil/cppwinrt_helpers.h b/Externals/WIL/include/wil/cppwinrt_helpers.h new file mode 100644 index 0000000000..6678b8f3bb --- /dev/null +++ b/Externals/WIL/include/wil/cppwinrt_helpers.h @@ -0,0 +1,352 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* + +#ifndef __WIL_CPPWINRT_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; +} + +#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +#include +namespace wil::details +{ + template using coroutine_handle = std::experimental::coroutine_handle; +} +#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L) +#include +namespace wil::details +{ + template using coroutine_handle = std::coroutine_handle; +} +#endif +/// @endcond + +#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (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; + }; +} +/// @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 }; + } +} +#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; + }; +} +#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; + }; +} +#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; + }; +} +#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; + }; +} +#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS +/// @endcond + +#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS) +#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS +namespace wil +{ + /// @cond + namespace details + { + template struct is_winrt_vector_like { + private: + template ().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 {}; + } + } + } + /// @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. + ~~~ + winrt::IVector collection = GetCollection(); + std::vector allData = wil::to_vector(collection); // read all data from collection + for (winrt::hstring const& item : allData) + { + // use item + } + ~~~ + 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); + } + 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); + break; + } + } + return result; + } + else + { + return to_vector(src.First()); + } + } +} +#endif + +#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS) +#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS +#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)*/ +} + +#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/Externals/WIL/include/wil/cppwinrt_wrl.h b/Externals/WIL/include/wil/cppwinrt_wrl.h new file mode 100644 index 0000000000..4f5fa36eb4 --- /dev/null +++ b/Externals/WIL/include/wil/cppwinrt_wrl.h @@ -0,0 +1,74 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_CPPWINRT_WRL_INCLUDED +#define __WIL_CPPWINRT_WRL_INCLUDED + +#include "cppwinrt.h" +#include + +#include "result_macros.h" +#include + +// wil::wrl_factory_for_winrt_com_class provides interopability between a +// C++/WinRT class and the WRL Module system, allowing the winrt class to be +// CoCreatable. +// +// Usage: +// - In your cpp, add: +// CoCreatableCppWinRtClass(className) +// +// - In the dll.cpp (or equivalent) for the module containing your class, add: +// CoCreatableClassWrlCreatorMapInclude(className) +// +namespace wil +{ + namespace details + { + template + class module_count_wrapper : public TCppWinRTClass + { + public: + module_count_wrapper() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->IncrementObjectCount(); + } + } + + virtual ~module_count_wrapper() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->DecrementObjectCount(); + } + } + }; + } + + template + class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> + { + public: + IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try + { + *object = nullptr; + RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); + + return winrt::make>().as(riid, object); + } + CATCH_RETURN() + }; +} + +#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) + +#endif // __WIL_CPPWINRT_WRL_INCLUDED diff --git a/Externals/WIL/include/wil/filesystem.h b/Externals/WIL/include/wil/filesystem.h index b74cd43086..b2bd2e7283 100644 --- a/Externals/WIL/include/wil/filesystem.h +++ b/Externals/WIL/include/wil/filesystem.h @@ -31,6 +31,7 @@ namespace wil 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) @@ -51,6 +52,7 @@ namespace wil } 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) @@ -83,7 +85,7 @@ namespace wil return false; } -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) // PathCch.h APIs are only in desktop API for now. @@ -111,7 +113,7 @@ namespace wil { if (::CreateDirectoryW(path, nullptr) == FALSE) { - DWORD const lastError = ::GetLastError(); + DWORD lastError = ::GetLastError(); if (lastError == ERROR_PATH_NOT_FOUND) { size_t parentLength; @@ -120,9 +122,16 @@ namespace wil wistd::unique_ptr parent(new (std::nothrow) wchar_t[parentLength + 1]); RETURN_IF_NULL_ALLOC(parent.get()); RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength)); - CreateDirectoryDeepNoThrow(parent.get()); // recurs + RETURN_IF_FAILED(CreateDirectoryDeepNoThrow(parent.get())); // recurs + } + if (::CreateDirectoryW(path, nullptr) == FALSE) + { + lastError = ::GetLastError(); + if (lastError != ERROR_ALREADY_EXISTS) + { + RETURN_WIN32(lastError); + } } - RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr)); } else if (lastError != ERROR_ALREADY_EXISTS) { @@ -183,13 +192,53 @@ namespace wil enum class RemoveDirectoryOptions { None = 0, - KeepRootDirectory = 0x1 + KeepRootDirectory = 0x1, + RemoveReadOnly = 0x2, }; DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions); + namespace details + { + // Reparse points should not be traversed in most recursive walks of the file system, + // unless allowed through the appropriate reparse tag. + inline bool CanRecurseIntoDirectory(const FILE_ATTRIBUTE_TAG_INFO& info) + { + return (WI_IsFlagSet(info.FileAttributes, FILE_ATTRIBUTE_DIRECTORY) && + (WI_IsFlagClear(info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) || + (IsReparseTagDirectory(info.ReparseTag) || (info.ReparseTag == IO_REPARSE_TAG_WCI)))); + } + } + + // Retrieve a handle to a directory only if it is safe to recurse into. + inline wil::unique_hfile TryCreateFileCanRecurseIntoDirectory(PCWSTR path, PWIN32_FIND_DATAW fileFindData, 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) WI_NOEXCEPT + 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; @@ -228,14 +277,50 @@ namespace wil PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete)); if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { - RemoveDirectoryOptions localOptions = options; - RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory))); + // 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 { - // note: if pathToDelete is read-only this will fail, consider adding - // RemoveDirectoryOptions::RemoveReadOnly to enable this behavior. - RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get())); + // 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())); + } } } @@ -252,7 +337,35 @@ namespace wil if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory)) { - RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get())); + 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; } @@ -552,7 +665,7 @@ namespace wil OVERLAPPED m_overlapped{}; TP_IO *m_tpIo = __nullptr; srwlock m_cancelLock; - char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow. + 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; } @@ -596,7 +709,6 @@ namespace wil auto readerState = static_cast(context); // WI_ASSERT(overlapped == &readerState->m_overlapped); - bool requeue = true; if (result == ERROR_SUCCESS) { for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast(readerState->m_readBuffer))) @@ -613,19 +725,17 @@ namespace wil } else { - requeue = false; + // No need to requeue + return; } - if (requeue) + // If the lock is held non-shared or the TP IO is nullptr, this + // structure is being torn down. Otherwise, monitor for further + // changes. + auto autoLock = readerState->m_cancelLock.try_lock_shared(); + if (autoLock && readerState->m_tpIo) { - // 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 - } + readerState->StartIo(); // ignoring failure here } } @@ -798,7 +908,7 @@ namespace wil // Type unsafe version used in the implementation to avoid template bloat. inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize, - _Outptr_result_nullonfailure_ void **result) + _Outptr_result_maybenull_ void **result) { *result = nullptr; @@ -875,6 +985,36 @@ namespace wil return S_OK; } + // Verifies that the given file path is not a hard or a soft link. If the file is present at the path, returns + // a handle to it without delete permissions to block an attacker from swapping the file. + inline HRESULT CreateFileAndEnsureNotLinked(PCWSTR path, wil::unique_hfile& fileHandle) + { + // Open handles to the original path and to the final path and compare each file's information + // to verify they are the same file. If they are different, the file is a soft link. + fileHandle.reset(CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr)); + RETURN_LAST_ERROR_IF(!fileHandle); + BY_HANDLE_FILE_INFORMATION fileInfo; + RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(fileHandle.get(), &fileInfo)); + + // Open a handle without the reparse point flag to get the final path in case it is a soft link. + wil::unique_hfile finalPathHandle(CreateFileW(path, 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + RETURN_LAST_ERROR_IF(!finalPathHandle); + BY_HANDLE_FILE_INFORMATION finalFileInfo; + RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(finalPathHandle.get(), &finalFileInfo)); + finalPathHandle.reset(); + + // The low and high indices and volume serial number uniquely identify a file. These must match if they are the same file. + const bool isSoftLink = + ((fileInfo.nFileIndexLow != finalFileInfo.nFileIndexLow) || + (fileInfo.nFileIndexHigh != finalFileInfo.nFileIndexHigh) || + (fileInfo.dwVolumeSerialNumber != finalFileInfo.dwVolumeSerialNumber)); + + // Return failure if it is a soft link or a hard link (number of links greater than 1). + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), (isSoftLink || fileInfo.nNumberOfLinks > 1)); + + return S_OK; + } + #ifdef _CPPUNWIND /** Get file information for a fixed sized structure, throws on failure. ~~~ @@ -902,7 +1042,7 @@ namespace wil return result; } #endif // _CPPUNWIND -#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) } #endif // __WIL_FILESYSTEM_INCLUDED diff --git a/Externals/WIL/include/wil/nt_result_macros.h b/Externals/WIL/include/wil/nt_result_macros.h new file mode 100644 index 0000000000..ddb9740b7b --- /dev/null +++ b/Externals/WIL/include/wil/nt_result_macros.h @@ -0,0 +1,168 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_NT_RESULTMACROS_INCLUDED +#define __WIL_NT_RESULTMACROS_INCLUDED + +#include "result_macros.h" + +// Helpers for return macros +#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } __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, fmt, ##__VA_ARGS__); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0) + +//***************************************************************************** +// Macros for returning failures as NTSTATUS +//***************************************************************************** + +// Always returns a known result (NTSTATUS) - always logs failures +#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status) + +// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure +#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__) + +// Conditionally returns failures (NTSTATUS) - always logs failures +#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) + +// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure +#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +//***************************************************************************** +// Macros to catch and convert exceptions on failure +//***************************************************************************** + +// Use these macros *within* a catch (...) block to handle exceptions +#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr)) +#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Use these macros in place of a catch block to handle exceptions +#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); } +#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } + + +namespace wil +{ + //***************************************************************************** + // Public Helpers that catch -- mostly only enabled when exceptions are enabled + //***************************************************************************** + + // StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally + // it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type + // the function will fail fast. + // + // try + // { + // // Code + // } + // catch (...) + // { + // status = wil::StatusFromCaughtException(); + // } + _Always_(_Post_satisfies_(return < 0)) + __declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT + { + bool isNormalized = false; + NTSTATUS status = STATUS_SUCCESS; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status; + } + if (FAILED_NTSTATUS(status)) + { + return status; + } + + // Caller bug: an unknown exception was thrown + __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); + return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + + namespace details + { + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); + + namespace __R_NS_NAME + { +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportStatus_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } + + __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportStatus_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + } + + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status; + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); + } + + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status; + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); + } + } +} + +#endif // __WIL_NT_RESULTMACROS_INCLUDED diff --git a/Externals/WIL/include/wil/resource.h b/Externals/WIL/include/wil/resource.h index a0e5741b09..192f25ad70 100644 --- a/Externals/WIL/include/wil/resource.h +++ b/Externals/WIL/include/wil/resource.h @@ -46,7 +46,7 @@ namespace wil //! to that value when it is destroyed. //! //! This is useful in library code that runs during a value's destructor. If the library code could - //! inadvertantly change the value of GetLastError (by calling a Win32 API or similar), it should + //! 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. //! @@ -63,12 +63,15 @@ namespace wil class last_error_context { #ifndef WIL_KERNEL_MODE - bool m_dismissed; - DWORD m_error; + bool m_dismissed = false; + DWORD m_error = 0; public: - last_error_context() WI_NOEXCEPT : - m_dismissed(false), - m_error(::GetLastError()) + last_error_context() WI_NOEXCEPT : last_error_context(::GetLastError()) + { + } + + explicit last_error_context(DWORD error) WI_NOEXCEPT : + m_error(error) { } @@ -77,7 +80,7 @@ namespace wil operator=(wistd::move(other)); } - last_error_context & operator=(last_error_context&& other) WI_NOEXCEPT + last_error_context& operator=(last_error_context&& other) WI_NOEXCEPT { m_dismissed = wistd::exchange(other.m_dismissed, true); m_error = other.m_error; @@ -100,6 +103,11 @@ namespace wil WI_ASSERT(!m_dismissed); m_dismissed = true; } + + auto value() const WI_NOEXCEPT + { + return m_error; + } #else public: void release() WI_NOEXCEPT { } @@ -113,46 +121,63 @@ namespace wil typedef wistd::integral_constant pointer_access_noaddress; // get() and release() are available typedef wistd::integral_constant pointer_access_none; // the raw pointer is not available - template // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer - struct resource_policy + template struct close_invoke_helper { - typedef pointer_storage pointer_storage; - typedef pointer pointer; - typedef pointer_invalid pointer_invalid; - typedef pointer_access pointer_access; - __forceinline static pointer_storage invalid_value() WI_NOEXCEPT { return (pointer)invalid; } - __forceinline static bool is_valid(pointer_storage value) WI_NOEXCEPT { return (static_cast(value) != (pointer)invalid); } - __forceinline static void close(pointer_storage value) WI_NOEXCEPT { wistd::invoke(close_fn, value); } - - inline static void close_reset(pointer_storage value) WI_NOEXCEPT + __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 // 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 + template class unique_storage { protected: - typedef policy policy; + 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()) { @@ -177,13 +202,6 @@ namespace wil } } - void replace(unique_storage &&other) WI_NOEXCEPT - { - reset(other.m_ptr); - other.m_ptr = policy::invalid_value(); - } - - public: bool is_valid() const WI_NOEXCEPT { return policy::is_valid(m_ptr); @@ -223,6 +241,13 @@ namespace wil 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; }; @@ -252,7 +277,8 @@ namespace wil // forwarding constructor: forwards all 'explicit' and multi-arg constructors to the base class template - explicit unique_any_t(arg1 && first, args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + 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 || @@ -410,7 +436,7 @@ namespace wil 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) + 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>>; @@ -663,7 +689,7 @@ namespace wil { } - out_param_t(out_param_t&& other) : + out_param_t(out_param_t&& other) WI_NOEXCEPT : wrapper(other.wrapper), pRaw(other.pRaw) { @@ -703,7 +729,7 @@ namespace wil { } - out_param_ptr_t(out_param_ptr_t&& other) : + out_param_ptr_t(out_param_ptr_t&& other) WI_NOEXCEPT : wrapper(other.wrapper), pRaw(other.pRaw) { @@ -741,7 +767,7 @@ namespace wil } /** Use to retrieve raw out parameter pointers (with a required cast) into smart pointers that do not support the '&' operator. - Use only when the smart pointer's &handle is not equal to the output type a function requries, necessitating a cast. + 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) @@ -766,7 +792,7 @@ namespace wil ~~~ typedef wil::unique_struct unique_prop_variant_default_init; - unique_prop_variant propvariant; + unique_prop_variant_default_init propvariant; SomeFunction(&propvariant); ~~~ @@ -781,6 +807,7 @@ namespace wil 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() @@ -813,7 +840,7 @@ namespace wil //! Calls the custom close function ~unique_struct() WI_NOEXCEPT { - wistd::invoke(close_fn, this); + closer::close(this); } void reset(const unique_struct&) = delete; @@ -821,10 +848,7 @@ namespace wil //! Resets this managed struct by calling the custom close function and begins management of the other struct void reset(const struct_t& other) WI_NOEXCEPT { - { - auto preserveError = last_error_context(); - wistd::invoke(close_fn, this); - } + closer::close_reset(this); struct_t::operator=(other); } @@ -832,7 +856,7 @@ namespace wil //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified void reset() WI_NOEXCEPT { - wistd::invoke(close_fn, this); + closer::close(this); call_init(use_default_init_fn()); } @@ -1142,7 +1166,7 @@ namespace wil { } - size_address_ptr(size_address_ptr&& other) : + size_address_ptr(size_address_ptr&& other) WI_NOEXCEPT : wrapper(other.wrapper), size(other.size) { @@ -1215,7 +1239,7 @@ namespace wil template void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T& p) const { - wistd::invoke(close_fn, &p); + close_invoker::close(&p); } }; @@ -1524,7 +1548,7 @@ namespace wil } if (oldSource) { - wistd::invoke(close_fn, oldSource); + details::close_invoker::close(oldSource); oldSource->Release(); } } @@ -1636,7 +1660,7 @@ namespace wil m_call = false; if (call) { - wistd::invoke(close_fn); + close_fn(); } } @@ -1770,7 +1794,7 @@ namespace wil 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)); + RETURN_IF_FAILED(StringCchVPrintfExW(buffer, lengthRequiredWithoutNull + 1, nullptr, nullptr, STRSAFE_NULL_ON_FAILURE, pszFormat, argsVL)); result = maker.release(); return S_OK; @@ -1780,7 +1804,7 @@ namespace wil // 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, _In_ ...) + HRESULT str_printf_nothrow(string_type& result, _Printf_format_string_ PCWSTR pszFormat, ...) { va_list argsVL; va_start(argsVL, pszFormat); @@ -1793,7 +1817,7 @@ namespace wil // 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, _In_ ...) + string_type str_printf(_Printf_format_string_ PCWSTR pszFormat, ...) { string_type result; va_list argsVL; @@ -1808,7 +1832,7 @@ namespace wil // 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, _In_ ...) + string_type str_printf_failfast(_Printf_format_string_ PCWSTR pszFormat, ...) { string_type result; va_list argsVL; @@ -1855,16 +1879,17 @@ namespace wil { // 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 + template class shared_storage { protected: - typedef unique_t unique_t; + 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) @@ -1904,12 +1929,6 @@ namespace wil { { } - void replace(shared_storage &&other) WI_NOEXCEPT - { - m_ptr = wistd::move(other.m_ptr); - } - - public: bool is_valid() const WI_NOEXCEPT { return (m_ptr && m_ptr->is_valid()); @@ -1959,6 +1978,12 @@ namespace wil { 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; @@ -1983,7 +2008,8 @@ namespace wil { // default and forwarding constructor: forwards default, all 'explicit' and multi-arg constructors to the base class template - explicit shared_any_t(args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + explicit shared_any_t(args_t&&... args) + __WI_NOEXCEPT_((wistd::is_nothrow_constructible_v)) : storage_t(wistd::forward(args)...) { } @@ -2147,11 +2173,11 @@ namespace wil { // This class provides weak_ptr<> support for shared_any<>, bringing the same weak reference counting and lock() acquire semantics // to shared_any. - template + template class weak_any { public: - typedef shared_t shared_t; + typedef SharedT shared_t; weak_any() WI_NOEXCEPT { @@ -2407,7 +2433,7 @@ namespace wil // Non-RTL implementation for threadpool_t parameter of DestroyThreadPoolTimer<> struct SystemThreadPoolMethods { - static void WINAPI SetThreadpoolTimer(_Inout_ PTP_TIMER Timer, _In_opt_ PFILETIME DueTime, _In_ DWORD Period, _In_opt_ DWORD WindowLength) WI_NOEXCEPT + 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); } @@ -2509,6 +2535,22 @@ namespace wil #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) typedef unique_any unique_sid; + typedef unique_any_handle_null_only unique_boundary_descriptor; + + namespace details + { + template + inline void __stdcall ClosePrivateNamespaceHelper(HANDLE h) WI_NOEXCEPT + { + ::ClosePrivateNamespace(h, flags); + } + } + + template + using unique_private_namespace = unique_any_handle_null_only), &details::ClosePrivateNamespaceHelper>; + + using unique_private_namespace_close = unique_private_namespace<>; + using unique_private_namespace_destroy = unique_private_namespace; #endif using unique_tool_help_snapshot = unique_hfile; @@ -2561,10 +2603,10 @@ namespace wil } // Waits on the given handle for the specified duration - inline bool handle_wait(HANDLE hEvent, DWORD dwMilliseconds = INFINITE) WI_NOEXCEPT + inline bool handle_wait(HANDLE hEvent, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) WI_NOEXCEPT { - DWORD status = ::WaitForSingleObjectEx(hEvent, dwMilliseconds, FALSE); - __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0)); + 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); } @@ -2626,29 +2668,29 @@ namespace wil } // Basic WaitForSingleObject on the event handle with the given timeout - bool wait(DWORD dwMilliseconds = INFINITE) const WI_NOEXCEPT + bool wait(DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) const WI_NOEXCEPT { - return wil::handle_wait(storage_t::get(), dwMilliseconds); + return wil::handle_wait(storage_t::get(), dwMilliseconds, bAlertable); } // Tries to create a named event -- returns false if unable to do so (gle may still be inspected with return=false) - bool try_create(EventOptions options, PCWSTR name, _In_opt_ LPSECURITY_ATTRIBUTES pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + bool try_create(EventOptions options, PCWSTR name, _In_opt_ LPSECURITY_ATTRIBUTES securityAttributes = nullptr, _Out_opt_ bool *alreadyExists = nullptr) { - auto handle = ::CreateEventExW(pSecurity, name, (WI_IsFlagSet(options, EventOptions::ManualReset) ? CREATE_EVENT_MANUAL_RESET : 0) | (WI_IsFlagSet(options, EventOptions::Signaled) ? CREATE_EVENT_INITIAL_SET : 0), EVENT_ALL_ACCESS); + 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(pAlreadyExists, false); + assign_to_opt_param(alreadyExists, false); return false; } - assign_to_opt_param(pAlreadyExists, (::GetLastError() == ERROR_ALREADY_EXISTS)); + 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 pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + 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, pSecurity, pAlreadyExists)); + 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) @@ -2676,7 +2718,7 @@ namespace wil typedef unique_any_t, err_exception_policy>> unique_event; #endif -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#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, @@ -2843,7 +2885,7 @@ namespace wil /** An alias for `wil::slim_event_auto_reset`. */ using slim_event = slim_event_auto_reset; -#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8) typedef unique_any mutex_release_scope_exit; @@ -2892,21 +2934,25 @@ namespace wil } // Tries to create a named mutex -- returns false if unable to do so (gle may still be inspected with return=false) - bool try_create(_In_opt_ PCWSTR name, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pMutexAttributes = nullptr) + 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(pMutexAttributes, name, dwFlags, desiredAccess); + 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 pMutexAttributes = nullptr) + 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, pMutexAttributes)); + 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) @@ -2983,21 +3029,23 @@ namespace wil } // 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) + 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) + 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)); + 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) @@ -3287,11 +3335,10 @@ namespace wil _When_((source != nullptr) && length == static_cast(-1), _In_z_) PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT { - // guard against invalid parameters (null source with -1 length) - FAIL_FAST_IF(!source && (length == static_cast(-1))); - if (length == static_cast(-1)) { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source); length = strlen(source); } const size_t cb = (length + 1) * sizeof(*source); @@ -3385,6 +3432,9 @@ namespace wil 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. @@ -3430,7 +3480,7 @@ namespace wil struct process_heap_deleter { template - void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const { details::FreeProcessHeap(p); } @@ -3439,7 +3489,7 @@ namespace wil struct virtualalloc_deleter { template - void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const { ::VirtualFree(p, 0, MEM_RELEASE); } @@ -3448,7 +3498,7 @@ namespace wil struct mapview_deleter { template - void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const { ::UnmapViewOfFile(p); } @@ -3481,7 +3531,7 @@ namespace wil /** Manages a typed pointer allocated with MapViewOfFile A specialization of wistd::unique_ptr<> that frees via UnmapViewOfFile(p). */ - template + template using unique_mapview_ptr = wistd::unique_ptr; #endif // __WIL_WINBASE_ @@ -3558,8 +3608,8 @@ namespace wil typedef typename err_policy::result result; // Exception-based constructors - template - event_watcher_t(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + 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)); @@ -4211,6 +4261,14 @@ namespace wil #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) @@ -4317,7 +4375,7 @@ namespace wil WI_NODISCARD inline unique_couninitialize_call CoInitializeEx_failfast(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) { FAIL_FAST_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); - return unique_couninitialize_call(); + return {}; } #endif // __WIL__COMBASEAPI_H_APP #if defined(__WIL__COMBASEAPI_H_APP) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__COMBASEAPI_H_APPEXCEPTIONAL) @@ -4325,7 +4383,7 @@ namespace wil WI_NODISCARD inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) { THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); - return unique_couninitialize_call(); + return {}; } #endif @@ -4460,6 +4518,8 @@ namespace wil 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 { @@ -4473,6 +4533,8 @@ namespace wil wchar_t* buffer() { WI_ASSERT(m_charBuffer != nullptr); return m_charBuffer; } const wchar_t* buffer() const { return m_charBuffer; } + HRESULT trim_at_existing_null(size_t length) { return make(buffer(), length); } + unique_hstring release() { m_charBuffer = nullptr; @@ -4554,6 +4616,33 @@ namespace wil } #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) #define __WIL_OLEAUTO_H_STL @@ -4727,12 +4816,47 @@ namespace wil } /// @endcond - typedef unique_any unique_cert_context; + struct cert_context_t : details::unique_storage> + { + // forward all base class constructors... + template + explicit cert_context_t(args_t&&... args) WI_NOEXCEPT : unique_storage(wistd::forward(args)...) {} + + /** A wrapper around CertEnumCertificatesInStore. + CertEnumCertificatesInStore takes ownership of its second paramter in an unclear fashion, + making it error-prone to use in combination with unique_cert_context. This wrapper helps + manage the resource correctly while ensuring the GetLastError state set by CertEnumCertificatesInStore. + is not lost. See MSDN for more information on `CertEnumCertificatesInStore`. + ~~~~ + void MyMethod(HCERTSTORE certStore) + { + wil::unique_cert_context enumCert; + while (enumCert.CertEnumCertificatesInStore(certStore)) + { + UseTheCertToDoTheThing(enumCert); + } + } + ~~~~ + @param certStore A handle of a certificate store. + @param 'true' if a certificate was enumerated by this call, false otherwise. + */ + bool CertEnumCertificatesInStore(HCERTSTORE certStore) WI_NOEXCEPT + { + reset(::CertEnumCertificatesInStore(certStore, release())); + return is_valid(); + } + }; + + // Warning - ::CertEnumCertificatesInStore takes ownership of its parameter. Prefer the + // .CertEnumCertificatesInStore method of the unique_cert_context or else use .release + // when calling ::CertEnumCertificatesInStore directly. + typedef unique_any_t unique_cert_context; typedef unique_any unique_cert_chain_context; typedef unique_any unique_hcertstore; typedef unique_any unique_hcryptprov; typedef unique_any unique_hcryptkey; typedef unique_any unique_hcrypthash; + typedef unique_any unique_hcryptmsg; #endif // __WIL__WINCRYPT_H__ #if defined(__WIL__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__STL) && defined(WIL_RESOURCE_STL) #define __WIL__WINCRYPT_H__STL @@ -4742,6 +4866,7 @@ namespace wil 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; @@ -4749,6 +4874,7 @@ namespace wil 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 @@ -5249,10 +5375,6 @@ namespace wil } } - explicit unique_hglobal_locked(unique_hglobal& global) : unique_hglobal_locked(global.get()) - { - } - explicit unique_hglobal_locked(STGMEDIUM& medium) : unique_hglobal_locked(medium.hGlobal) { } @@ -5305,6 +5427,14 @@ namespace wil 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) +#define __WIL_INC_USERENV + typedef unique_any unique_environment_block; +#endif // __WIL_INC_USERENV +#pragma warning(pop) + #if defined(_WINSVC_) && !defined(__WIL_HANDLE_H_WINSVC) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) #define __WIL_HANDLE_H_WINSVC typedef unique_any unique_schandle; @@ -5328,6 +5458,16 @@ namespace wil typedef weak_any weak_file; #endif // __WIL__INC_STDIO_STL +#if defined(_INC_LOCALE) && !defined(__WIL_INC_LOCALE) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_LOCALE + 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) +#define __WIL__INC_LOCALE_STL + 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) #define __WIL_NTLSA_ typedef unique_any unique_hlsa; @@ -5497,7 +5637,7 @@ namespace wil ~~~ unique_process_information process; CreateProcessW(..., CREATE_SUSPENDED, ..., &process); - THROW_IF_WIN32_BOOL_FALSE(ResumeThread(process.hThread)); + THROW_LAST_ERROR_IF(ResumeThread(process.hThread) == -1); THROW_LAST_ERROR_IF(WaitForSingleObject(process.hProcess, INFINITE) != WAIT_OBJECT_0); ~~~ */ @@ -5628,7 +5768,6 @@ namespace wil 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. @@ -5654,7 +5793,6 @@ namespace wil 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. @@ -5685,54 +5823,382 @@ namespace wil using unique_wdf_wait_lock = unique_any_t; using unique_wdf_spin_lock = unique_any_t; - template - struct wdf_object_reference + //! 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 { - TWDFOBJECT wdfObject = WDF_NO_HANDLE; - PVOID tag = nullptr; + public: + unique_wdf_object_reference() WI_NOEXCEPT = default; - wdf_object_reference() WI_NOEXCEPT = default; - - wdf_object_reference(TWDFOBJECT wdfObject, PVOID tag = nullptr) WI_NOEXCEPT - : wdfObject(wdfObject), tag(tag) + //! 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) { } - operator TWDFOBJECT() const WI_NOEXCEPT + //! 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(); + } + + explicit operator bool() const WI_NOEXCEPT + { + return m_wdfObject != WDF_NO_HANDLE; + } + + wdf_object_t get() const WI_NOEXCEPT + { + return m_wdfObject; + } + + 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; } - static void close(const wdf_object_reference& wdfObjectReference) WI_NOEXCEPT + void swap(unique_wdf_object_reference& other) WI_NOEXCEPT { - // We don't use WdfObjectDereferenceActual because there is no way to provide the - // correct __LINE__ and __FILE__, but if you use RAII all the way, you shouldn't have to - // worry about where it was released, only where it was acquired. - WdfObjectDereferenceWithTag(wdfObjectReference.wdfObject, wdfObjectReference.tag); + 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; }; - template - using unique_wdf_object_reference = unique_any::close), - &wdf_object_reference::close, details::pointer_access_noaddress, wdf_object_reference>; - - // Increment the ref-count on a WDF object a unique_wdf_object_reference for it. Use + // 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 - inline WI_NODISCARD unique_wdf_object_reference wdf_object_reference_increment( - TWDFOBJECT wdfObject, PVOID tag, LONG lineNumber, PCSTR fileName) WI_NOEXCEPT + template + inline WI_NODISCARD 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{ wdf_object_reference{ wdfObject, tag } }; + return unique_wdf_object_reference{ wdfObject, tag }; + } + + template + inline WI_NODISCARD 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(); + } + + 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 + + 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) && \ @@ -5801,8 +6267,8 @@ namespace wil using kspin_lock_at_dpc_guard = unique_any; - inline WI_NODISCARD + inline _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ _IRQL_raises_(DISPATCH_LEVEL) @@ -5815,8 +6281,8 @@ namespace wil return kspin_lock_guard(spinLockSavedIrql); } - inline WI_NODISCARD + inline _IRQL_requires_min_(DISPATCH_LEVEL) kspin_lock_at_dpc_guard acquire_kspin_lock_at_dpc(_In_ PKSPIN_LOCK spinLock) @@ -5828,7 +6294,6 @@ namespace wil class kernel_spin_lock { public: - kernel_spin_lock() WI_NOEXCEPT { ::KeInitializeSpinLock(&m_kSpinLock); @@ -5859,7 +6324,6 @@ namespace wil } private: - KSPIN_LOCK m_kSpinLock; }; @@ -5869,7 +6333,6 @@ namespace wil class kernel_event_t { public: - explicit kernel_event_t(bool isSignaled = false) WI_NOEXCEPT { ::KeInitializeEvent(&m_kernelEvent, static_cast(eventType), isSignaled ? TRUE : FALSE); @@ -5926,7 +6389,6 @@ namespace wil } private: - bool wait_for_single_object(_In_opt_ LARGE_INTEGER* waitDuration) WI_NOEXCEPT { auto status = ::KeWaitForSingleObject(&m_kernelEvent, Executive, KernelMode, FALSE, waitDuration); @@ -5945,6 +6407,224 @@ namespace wil using kernel_event_manual_reset = details::kernel_event_t; using kernel_event = kernel_event_auto_reset; // For parity with the default for other WIL event types. + /** + RAII class and lock-guards for a kernel FAST_MUTEX. + */ + + using fast_mutex_guard = unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard acquire_fast_mutex(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::ExAcquireFastMutex(fastMutex); + return fast_mutex_guard(fastMutex); + } + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard try_acquire_fast_mutex(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + if (::ExTryToAcquireFastMutex(fastMutex)) + { + return fast_mutex_guard(fastMutex); + } + else + { + return fast_mutex_guard(); + } + } + + class fast_mutex + { + public: + fast_mutex() WI_NOEXCEPT + { + ::ExInitializeFastMutex(&m_fastMutex); + } + + ~fast_mutex() WI_NOEXCEPT = default; + + // Cannot change memory location. + fast_mutex(const fast_mutex&) = delete; + fast_mutex& operator=(const fast_mutex&) = delete; + fast_mutex(fast_mutex&&) = delete; + fast_mutex& operator=(fast_mutex&&) = delete; + + // Calls ExAcquireFastMutex. Returned wil::unique_any object calls ExReleaseFastMutex on + // destruction. + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard acquire() WI_NOEXCEPT + { + return acquire_fast_mutex(&m_fastMutex); + } + + // Calls ExTryToAcquireFastMutex. Returned wil::unique_any may be empty. If non-empty, it + // calls ExReleaseFastMutex on destruction. + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard try_acquire() WI_NOEXCEPT + { + return try_acquire_fast_mutex(&m_fastMutex); + } + + private: + FAST_MUTEX m_fastMutex; + }; + + namespace details + { + _IRQL_requires_max_(APC_LEVEL) + inline void release_fast_mutex_with_critical_region(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::ExReleaseFastMutexUnsafe(fastMutex); + ::KeLeaveCriticalRegion(); + } + } + + using fast_mutex_with_critical_region_guard = + unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_with_critical_region_guard acquire_fast_mutex_with_critical_region(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquireFastMutexUnsafe(fastMutex); + return fast_mutex_with_critical_region_guard(fastMutex); + } + + // A FAST_MUTEX lock class that calls KeEnterCriticalRegion and then ExAcquireFastMutexUnsafe. + // Returned wil::unique_any lock-guard calls ExReleaseFastMutexUnsafe and KeLeaveCriticalRegion + // on destruction. This is useful if calling code wants to stay at PASSIVE_LEVEL. + class fast_mutex_with_critical_region + { + public: + fast_mutex_with_critical_region() WI_NOEXCEPT + { + ::ExInitializeFastMutex(&m_fastMutex); + } + + ~fast_mutex_with_critical_region() WI_NOEXCEPT = default; + + // Cannot change memory location. + fast_mutex_with_critical_region(const fast_mutex_with_critical_region&) = delete; + fast_mutex_with_critical_region& operator=(const fast_mutex_with_critical_region&) = delete; + fast_mutex_with_critical_region(fast_mutex_with_critical_region&&) = delete; + fast_mutex_with_critical_region& operator=(fast_mutex_with_critical_region&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_with_critical_region_guard acquire() WI_NOEXCEPT + { + return acquire_fast_mutex_with_critical_region(&m_fastMutex); + } + + private: + FAST_MUTEX m_fastMutex; + }; + + //! A type that calls KeLeaveCriticalRegion on destruction (or reset()). + using unique_leave_critical_region_call = unique_call; + + //! Disables user APCs and normal kernel APCs; returns an RAII object that reverts + WI_NODISCARD inline unique_leave_critical_region_call enter_critical_region() + { + KeEnterCriticalRegion(); + return{}; + } + + //! A type that calls KeLeaveGuardedRegion on destruction (or reset()). + using unique_leave_guarded_region_call = unique_call; + + //! Disables all APCs; returns an RAII object that reverts + WI_NODISCARD inline unique_leave_guarded_region_call enter_guarded_region() + { + KeEnterGuardedRegion(); + return{}; + } + + namespace details + { + _IRQL_requires_max_(APC_LEVEL) + inline void release_push_lock_exclusive(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::ExReleasePushLockExclusive(pushLock); + ::KeLeaveCriticalRegion(); + } + + _IRQL_requires_max_(APC_LEVEL) + inline void release_push_lock_shared(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::ExReleasePushLockShared(pushLock); + ::KeLeaveCriticalRegion(); + } + } + + using push_lock_exclusive_guard = + unique_any; + + using push_lock_shared_guard = + unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + push_lock_exclusive_guard acquire_push_lock_exclusive(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquirePushLockExclusive(pushLock); + return push_lock_exclusive_guard(pushLock); + } + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + push_lock_shared_guard acquire_push_lock_shared(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquirePushLockShared(pushLock); + return push_lock_shared_guard(pushLock); + } + + class push_lock + { + public: + push_lock() WI_NOEXCEPT + { + ::ExInitializePushLock(&m_pushLock); + } + + ~push_lock() WI_NOEXCEPT = default; + + // Cannot change memory location. + push_lock(const push_lock&) = delete; + push_lock& operator=(const push_lock&) = delete; + push_lock(push_lock&&) = delete; + push_lock& operator=(push_lock&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + push_lock_exclusive_guard acquire_exclusive() WI_NOEXCEPT + { + return acquire_push_lock_exclusive(&m_pushLock); + } + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + push_lock_shared_guard acquire_shared() WI_NOEXCEPT + { + return acquire_push_lock_shared(&m_pushLock); + } + + private: + EX_PUSH_LOCK m_pushLock; + }; + namespace details { // Define a templated type for pool functions in order to satisfy overload resolution below @@ -5979,6 +6659,422 @@ namespace wil #endif // __WIL_RESOURCE_ZWAPI +#if defined(WINTRUST_H) && defined(SOFTPUB_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_WINTRUST) +#define __WIL_WINTRUST + namespace details + { + inline void __stdcall CloseWintrustData(_Inout_ WINTRUST_DATA* wtData) WI_NOEXCEPT + { + GUID guidV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; + wtData->dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidV2, wtData); + } + } + typedef wil::unique_struct unique_wintrust_data; +#endif // __WIL_WINTRUST + +#if defined(MSCAT_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_MSCAT) +#define __WIL_MSCAT + namespace details + { + inline void __stdcall CryptCATAdminReleaseContextNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ HCATADMIN handle) WI_NOEXCEPT + { + CryptCATAdminReleaseContext(handle, 0); + } + } + typedef wil::unique_any unique_hcatadmin; + +#if defined(WIL_RESOURCE_STL) + typedef shared_any shared_hcatadmin; + struct hcatinfo_deleter + { + hcatinfo_deleter(wil::shared_hcatadmin handle) WI_NOEXCEPT : m_hCatAdmin(wistd::move(handle)) {} + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ HCATINFO handle) const WI_NOEXCEPT + { + CryptCATAdminReleaseCatalogContext(m_hCatAdmin.get(), handle, 0); + } + wil::shared_hcatadmin m_hCatAdmin; + }; + // This stores HCATINFO, i.e. HANDLE (void *) + typedef wistd::unique_ptr unique_hcatinfo; + + namespace details + { + class crypt_catalog_enumerator + { + wil::unique_hcatinfo m_hCatInfo; + const BYTE * m_hash; + DWORD m_hashLen; + bool m_initialized = false; + + struct ref + { + explicit ref(crypt_catalog_enumerator &r) WI_NOEXCEPT : + m_r(r) + {} + + operator HCATINFO() const WI_NOEXCEPT + { + return m_r.current(); + } + + wil::unique_hcatinfo move_from_unique_hcatinfo() WI_NOEXCEPT + { + wil::unique_hcatinfo info(wistd::move(m_r.m_hCatInfo)); + return info; + } + + bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return m_r.m_hCatInfo == nullptr; + } + + bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + private: + crypt_catalog_enumerator &m_r; + }; + + struct iterator + { +#ifdef _XUTILITY_ + // muse be input_iterator_tag as use of one instance invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + + explicit iterator(crypt_catalog_enumerator *r) WI_NOEXCEPT : + m_r(r) + {} + + iterator(const iterator &) = default; + iterator(iterator &&) = default; + iterator &operator=(const iterator &) = default; + iterator &operator=(iterator &&) = default; + + bool operator==(const iterator &rhs) const WI_NOEXCEPT + { + if (rhs.m_r == m_r) + { + return true; + } + + return (*this == nullptr) && (rhs == nullptr); + } + + bool operator!=(const iterator &rhs) const WI_NOEXCEPT + { + return !(rhs == *this); + } + + bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return nullptr == m_r || nullptr == m_r->current(); + } + + bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + iterator &operator++() WI_NOEXCEPT + { + if (m_r != nullptr) + { + m_r->next(); + } + + return *this; + } + + ref operator*() const WI_NOEXCEPT + { + return ref(*m_r); + } + + private: + crypt_catalog_enumerator *m_r; + }; + + shared_hcatadmin &hcatadmin() WI_NOEXCEPT + { + return m_hCatInfo.get_deleter().m_hCatAdmin; + } + + bool move_next() WI_NOEXCEPT + { + HCATINFO prevCatInfo = m_hCatInfo.release(); + m_hCatInfo.reset( + ::CryptCATAdminEnumCatalogFromHash( + hcatadmin().get(), + const_cast(m_hash), + m_hashLen, + 0, + &prevCatInfo)); + return !!m_hCatInfo; + } + + HCATINFO next() WI_NOEXCEPT + { + if (m_initialized && m_hCatInfo) + { + move_next(); + } + + return current(); + } + + HCATINFO init() WI_NOEXCEPT + { + if (!m_initialized) + { + m_initialized = true; + move_next(); + } + + return current(); + } + + HCATINFO current() WI_NOEXCEPT + { + return m_hCatInfo.get(); + } + + public: + crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + const BYTE *hash, + DWORD hashLen) WI_NOEXCEPT : + m_hCatInfo(nullptr, hCatAdmin), + m_hash(hash), + m_hashLen(hashLen) + // , m_initialized(false) // redundant + {} + + iterator begin() WI_NOEXCEPT + { + init(); + return iterator(this); + } + + iterator end() const WI_NOEXCEPT + { + return iterator(nullptr); + } + + crypt_catalog_enumerator(crypt_catalog_enumerator &&) = default; + crypt_catalog_enumerator &operator=(crypt_catalog_enumerator &&) = default; + + crypt_catalog_enumerator(const crypt_catalog_enumerator &) = delete; + crypt_catalog_enumerator &operator=(const crypt_catalog_enumerator &) = delete; + }; + } + + /** Use to enumerate catalogs containing a hash with a range-based for. + This avoids handling a raw resource to call CryptCATAdminEnumCatalogFromHash correctly. + Example: + `for (auto&& cat : wil::make_catalog_enumerator(hCatAdmin, hash, hashLen)) + { CryptCATCatalogInfoFromContext(cat, &catInfo, 0); }` */ + inline details::crypt_catalog_enumerator make_crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + _In_count_(hashLen) const BYTE *hash, DWORD hashLen) WI_NOEXCEPT + { + return details::crypt_catalog_enumerator(hCatAdmin, hash, hashLen); + } + + template + details::crypt_catalog_enumerator make_crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + const BYTE (&hash)[Size]) WI_NOEXCEPT + { + static_assert(Size <= static_cast(0xffffffffUL), "Array size truncated"); + return details::crypt_catalog_enumerator(hCatAdmin, hash, static_cast(Size)); + } + +#endif // WI_RESOURCE_STL + +#endif // __WIL_MSCAT + + +#if !defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL_RESOURCE_LOCK_ENFORCEMENT + + /** + 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 intentially 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; + + /** + Stands as proof that a shared lock has been acquired. See write_lock_required for more information. + */ + struct read_lock_required; + + 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 aquisition, 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; + }; + } + + 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 + }; + + 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 + + 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 {}; + } + +#endif //__WIL__RESOURCE_LOCKPROOF_WINBASE + +#if defined(_MUTEX_) && !defined(__WIL__RESOURCE_LOCKPROOF_MUTEX) && defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL__RESOURCE_LOCKPROOF_MUTEX + + namespace details + { + template + struct lock_proof_traits> : exclusive_lock_proof {}; + + template + struct lock_proof_traits> : exclusive_lock_proof {}; + } + +#endif //__WIL__RESOURCE_LOCKPROOF_MUTEX + +#if defined(_SHARED_MUTEX_) && !defined(__WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX) && defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX + + namespace details + { + template + struct lock_proof_traits> : shared_lock_proof {}; + } + +#endif //__WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX + } // namespace wil #pragma warning(pop) diff --git a/Externals/WIL/include/wil/result.h b/Externals/WIL/include/wil/result.h index f3b61e6054..9a768bc404 100644 --- a/Externals/WIL/include/wil/result.h +++ b/Externals/WIL/include/wil/result.h @@ -252,7 +252,8 @@ namespace wil if (ProcessShutdownInProgress()) { // There are no other threads to contend with. - if (--m_refCount == 0) + m_refCount = m_refCount - 1; + if (m_refCount == 0) { m_data.ProcessShutdown(); } @@ -260,7 +261,8 @@ namespace wil else { auto lock = m_mutex.acquire(); - if (--m_refCount == 0) + m_refCount = m_refCount - 1; + if (m_refCount == 0) { // We must explicitly destroy our semaphores while holding the mutex m_value.Destroy(); @@ -281,7 +283,7 @@ namespace wil const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); wchar_t name[MAX_PATH]; - WI_VERIFY(SUCCEEDED(StringCchPrintfW(name, ARRAYSIZE(name), L"Local\\SM0:%d:%d:%hs", ::GetCurrentProcessId(), size, staticNameWithVersion))); + 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)); @@ -295,7 +297,7 @@ namespace wil if (pointer) { *data = reinterpret_cast*>(pointer); - (*data)->m_refCount++; + (*data)->m_refCount = (*data)->m_refCount + 1; } else { @@ -312,13 +314,13 @@ namespace wil SemaphoreValue m_value; T m_data; - static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, ProcessLocalStorageData** 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(static_cast*>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size))); + unique_process_heap_ptr> dataAlloc(static_cast*>(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, size))); __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(dataAlloc); SemaphoreValue semaphoreValue; @@ -406,10 +408,9 @@ namespace wil if (shouldAllocate) { - Node *pNew = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(Node))); - if (pNew != nullptr) + if (auto pNewRaw = details::ProcessHeapAlloc(0, sizeof(Node))) { - new(pNew)Node{ threadId }; + auto pNew = new (pNewRaw) Node{ threadId }; Node *pFirst; do @@ -428,7 +429,7 @@ namespace wil struct Node { - DWORD threadId; + DWORD threadId = ULONG_MAX; Node* pNext = nullptr; T value{}; }; @@ -487,7 +488,7 @@ namespace wil if (!stringBuffer || (stringBufferSize < neededSize)) { - auto newBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, neededSize); + auto newBuffer = details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, neededSize); if (newBuffer) { ::HeapFree(::GetProcessHeap(), 0, stringBuffer); @@ -508,7 +509,7 @@ namespace wil } } - void Get(FailureInfo& info) + void Get(FailureInfo& info) const { ::ZeroMemory(&info, sizeof(info)); @@ -565,7 +566,7 @@ namespace wil if (!errors && create) { const unsigned short errorCount = 5; - errors = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo))); + errors = reinterpret_cast(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo))); if (errors) { errorAllocCount = errorCount; @@ -611,7 +612,7 @@ namespace wil errors[errorCurrentIndex].Set(info, ::InterlockedIncrementNoFence(failureSequenceId)); } - bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) + bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) const { if (!errors) { @@ -678,7 +679,7 @@ namespace wil // NOTE: FailureType::Log as it's only informative (no action) and SupportedExceptions::All as it's not a barrier, only recognition. wchar_t message[2048]; message[0] = L'\0'; - const HRESULT hr = details::ReportFailure_CaughtExceptionCommon(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All); + 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); @@ -958,7 +959,7 @@ namespace wil m_ppThreadList = nullptr; } - bool IsWatching() + bool IsWatching() const { return (m_threadId != 0); } @@ -1044,7 +1045,9 @@ namespace wil if (g_pfnTelemetryCallback != nullptr) { - g_pfnTelemetryCallback(reportedTelemetry, *pFailure); + // 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); } } @@ -1079,7 +1082,7 @@ namespace wil { } - bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT override { return m_errorFunction(failure); } @@ -1251,7 +1254,7 @@ namespace wil return (FAILED(m_failure.GetFailureInfo().hr) ? &(m_failure.GetFailureInfo()) : nullptr); } - bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + 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). diff --git a/Externals/WIL/include/wil/result_macros.h b/Externals/WIL/include/wil/result_macros.h index d96bf6c5b7..6cb8e30de6 100644 --- a/Externals/WIL/include/wil/result_macros.h +++ b/Externals/WIL/include/wil/result_macros.h @@ -20,9 +20,11 @@ #include #endif -// Setup the debug behavior +// 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(NDEBUG) +#if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(RESULT_NO_DEBUG) && (defined(WIL_KERNEL_MODE) || !defined(NDEBUG)) #define RESULT_DEBUG #endif #endif @@ -38,7 +40,7 @@ // 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 +#define __WI_ANALYSIS_ASSUME(_exp) ((void)sizeof(!(_exp))) // Validate syntax on non-debug builds #else #define __WI_ANALYSIS_ASSUME(_exp) __noop(_exp) #endif @@ -83,6 +85,9 @@ typedef _Return_type_success_(return >= 0) LONG NTSTATUS; #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 @@ -227,6 +232,9 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0") #ifndef RESULT_NORETURN_NULL #define RESULT_NORETURN_NULL _Ret_notnull_ #endif +#ifndef RESULT_NORETURN_RESULT +#define RESULT_NORETURN_RESULT(expr) (void)(expr); +#endif //***************************************************************************** // Helpers to setup the macros and functions used below... do not directly use. @@ -917,6 +925,16 @@ namespace wil 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 @@ -943,7 +961,9 @@ namespace wil 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 @@ -1038,6 +1058,9 @@ namespace wil // 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, ...) { @@ -1081,10 +1104,24 @@ namespace wil pszType = "Exception"; break; case FailureType::Return: - pszType = "ReturnHr"; + if (WI_IsFlagSet(failure.flags, FailureFlags::NtStatus)) + { + pszType = "ReturnNt"; + } + else + { + pszType = "ReturnHr"; + } break; case FailureType::Log: - pszType = "LogHr"; + if (WI_IsFlagSet(failure.flags, FailureFlags::NtStatus)) + { + pszType = "LogNt"; + } + else + { + pszType = "LogHr"; + } break; case FailureType::FailFast: pszType = "FailFast"; @@ -1093,7 +1130,21 @@ namespace wil wchar_t szErrorText[256]; szErrorText[0] = L'\0'; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, failure.hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorText, ARRAYSIZE(szErrorText), nullptr); + 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)] @@ -1115,7 +1166,7 @@ namespace wil 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(), failure.hr, szErrorText); + 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)) { @@ -1166,10 +1217,35 @@ namespace wil 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 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 @@ -1205,7 +1281,7 @@ namespace wil __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) HRESULT(__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; + 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; @@ -1218,6 +1294,40 @@ namespace wil // 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, @@ -1294,25 +1404,25 @@ namespace wil template struct return_type { - typedef tag_return_other type; + using type = tag_return_other; }; template <> struct return_type { - typedef tag_return_HRESULT type; + using type = tag_return_HRESULT; }; template <> struct return_type { - typedef tag_return_void type; + using type = tag_return_void; }; template <> struct return_type { - typedef tag_return_void type; + using type = tag_return_void; }; template @@ -1334,18 +1444,18 @@ namespace wil _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_If)(bool condition) WI_NOEXCEPT; } - __declspec(noreturn) inline void __stdcall WilFailFast(const FailureInfo& info); - inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, + RESULT_NORETURN inline void __stdcall WilFailFast(const FailureInfo& info); + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message, bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, _Out_ FailureInfo *failure) WI_NOEXCEPT; - __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); - template - __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + template + __declspec(noinline) inline void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); template inline void ReportFailure_ReplaceMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, ...); - __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr); + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr); template __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr); template @@ -1645,7 +1755,10 @@ namespace wil // 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; @@ -1676,8 +1789,9 @@ namespace wil s_hrErrorLast = hr; return 1; } +__WI_POP_WARNINGS - inline __declspec(noreturn) void __stdcall WilRaiseFailFastException(_In_ PEXCEPTION_RECORD er, _In_opt_ PCONTEXT cr, _In_ DWORD flags) + 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. @@ -1796,6 +1910,117 @@ namespace wil 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_ @@ -1821,6 +2046,12 @@ namespace wil 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 { @@ -1888,7 +2119,7 @@ namespace wil _Must_inspect_result_ STRSAFEAPI StringCchLengthA(_In_reads_or_z_(cchMax) STRSAFE_PCNZCH psz, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax, _Out_opt_ _Deref_out_range_(<, cchMax) _Deref_out_range_(<= , _String_length_(psz)) size_t* pcchLength) { HRESULT hr; - if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH)) + if ((psz == nullptr) || (cchMax > STRSAFE_MAX_CCH)) { hr = STRSAFE_E_INVALID_PARAMETER; } @@ -1918,11 +2149,10 @@ namespace wil { HRESULT hr = S_OK; int iRet; - size_t cchMax; - size_t cchNewDestLength = 0; // leave the last space for the null terminator - cchMax = cchDest - 1; + 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) @@ -1975,7 +2205,7 @@ namespace wil { va_list argList; va_start(argList, pszFormat); - hr = wil::details::WilStringVPrintfWorkerA(pszDest, cchDest, NULL, pszFormat, argList); + hr = wil::details::WilStringVPrintfWorkerA(pszDest, cchDest, nullptr, pszFormat, argList); va_end(argList); } else if (cchDest > 0) @@ -2031,7 +2261,7 @@ namespace wil { size_t cchLen = UntrustedStringLength(reinterpret_cast(pStart), (pEnd - pStart) / sizeof((*ppszBufferString)[0])); *ppszBufferString = (cchLen > 0) ? reinterpret_cast(pStart) : nullptr; - auto pReturn = min(pEnd, pStart + ((cchLen + 1) * sizeof((*ppszBufferString)[0]))); + auto pReturn = (wistd::min)(pEnd, pStart + ((cchLen + 1) * sizeof((*ppszBufferString)[0]))); __analysis_assume((pReturn >= pStart) && (pReturn <= pEnd)); return pReturn; } @@ -2112,6 +2342,10 @@ namespace wil 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; @@ -2157,6 +2391,9 @@ namespace wil 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 { @@ -2186,6 +2423,9 @@ namespace wil } } + 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 { @@ -2264,6 +2504,17 @@ namespace wil 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. @@ -2364,6 +2615,14 @@ namespace wil return hr; } + //! Returns the failed NTSTATUS that this exception represents. + _Always_(_Post_satisfies_(return < 0)) NTSTATUS GetStatusCode() const WI_NOEXCEPT + { + NTSTATUS const status = m_failure.GetFailureInfo().status; + __analysis_assume(status < 0); + return status; + } + //! Get a reference to the stored FailureInfo. FailureInfo const & GetFailureInfo() const WI_NOEXCEPT { @@ -2431,7 +2690,7 @@ namespace wil HRESULT hr = S_OK; if (details::g_pfnResultFromCaughtExceptionInternal) { - hr = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized); + hr = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).hr; } if (FAILED(hr)) { @@ -2474,7 +2733,7 @@ namespace wil // Private helpers to catch and propagate exceptions //***************************************************************************** - __declspec(noreturn) inline void TerminateAndReportError(_In_opt_ PEXCEPTION_POINTERS) + 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 @@ -2506,7 +2765,7 @@ namespace wil message[0] = L'\0'; MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); auto hr = exception.GetErrorCode(); - wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); return hr; } @@ -2516,7 +2775,7 @@ namespace wil message[0] = L'\0'; MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); constexpr auto hr = E_OUTOFMEMORY; - wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); return hr; } @@ -2526,7 +2785,7 @@ namespace wil message[0] = L'\0'; MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); constexpr auto hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); - ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); return hr; } @@ -2540,7 +2799,7 @@ namespace wil auto hr = g_pfnResultFromCaughtException_CppWinRt(message, ARRAYSIZE(message), &ignored); if (FAILED(hr)) { - ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); return hr; } } @@ -2572,6 +2831,7 @@ namespace wil } catch (...) { + // Fall through to returning 'hr' below } } @@ -2624,7 +2884,7 @@ namespace wil message[0] = L'\0'; MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); auto hr = exception->HResult; - wil::details::ReportFailure(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), hr, message); + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); return hr; } @@ -2644,6 +2904,9 @@ namespace wil 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; } @@ -2675,6 +2938,9 @@ namespace wil 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; } @@ -2804,7 +3070,7 @@ namespace wil throw ResultException(failure); } - __declspec(noinline) inline HRESULT __stdcall ResultFromCaughtExceptionInternal(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT + __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) { @@ -2814,12 +3080,17 @@ namespace wil if (details::g_pfnResultFromCaughtException_CppWinRt != nullptr) { - RETURN_IF_FAILED_EXPECTED(details::g_pfnResultFromCaughtException_CppWinRt(debugString, debugStringChars, isNormalized)); + const auto hr = details::g_pfnResultFromCaughtException_CppWinRt(debugString, debugStringChars, isNormalized); + if (FAILED(hr)) + { + return ResultStatus::FromResult(hr); + } } if (details::g_pfnResultFromCaughtException_WinRt != nullptr) { - return details::g_pfnResultFromCaughtException_WinRt(debugString, debugStringChars, isNormalized); + const auto hr = details::g_pfnResultFromCaughtException_WinRt(debugString, debugStringChars, isNormalized); + return ResultStatus::FromResult(hr); } if (g_pfnResultFromCaughtException) @@ -2832,19 +3103,19 @@ namespace wil { *isNormalized = true; MaybeGetExceptionString(exception, debugString, debugStringChars); - return exception.GetErrorCode(); + return ResultStatus::FromFailureInfo(exception.GetFailureInfo()); } catch (const std::bad_alloc& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); - return E_OUTOFMEMORY; + return ResultStatus::FromResult(E_OUTOFMEMORY); } catch (...) { auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); if (FAILED(hr)) { - return hr; + return ResultStatus::FromResult(hr); } } } @@ -2858,17 +3129,17 @@ namespace wil { *isNormalized = true; MaybeGetExceptionString(exception, debugString, debugStringChars); - return exception.GetErrorCode(); + return ResultStatus::FromFailureInfo(exception.GetFailureInfo()); } catch (const std::bad_alloc& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); - return E_OUTOFMEMORY; + return ResultStatus::FromResult(E_OUTOFMEMORY); } catch (std::exception& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); - return __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + return ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); } catch (...) { @@ -2877,7 +3148,7 @@ namespace wil } // Tell the caller that we were unable to map the exception by succeeding... - return S_OK; + return ResultStatus::FromResult(S_OK); } // Runs the given functor, converting any exceptions of the supported types that are known to HRESULTs and returning @@ -2981,6 +3252,7 @@ namespace wil __except (wil::details::TerminateAndReportError(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) { WI_ASSERT(false); + RESULT_NORETURN_RESULT(HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); } } @@ -3209,7 +3481,7 @@ namespace wil // Shared Reporting -- all reporting macros bubble up through this codepath //***************************************************************************** - inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message, bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, _Out_ FailureInfo *failure) WI_NOEXCEPT @@ -3219,17 +3491,20 @@ namespace wil static long volatile s_failureId = 0; + failure->hr = resultPair.hr; + failure->status = resultPair.status; + int failureCount = 0; switch (type) { case FailureType::Exception: - failureCount = RecordException(hr); + failureCount = RecordException(failure->hr); break; case FailureType::Return: - failureCount = RecordReturn(hr); + failureCount = RecordReturn(failure->hr); break; case FailureType::Log: - if (SUCCEEDED(hr)) + 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: @@ -3238,17 +3513,19 @@ namespace wil // LOG_IF_FAILED(hr); WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. Do not LOG_XXX success."); - hr = __HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE); + failure->hr = __HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE); + failure->status = wil::details::HrToNtStatus(failure->hr); } - failureCount = RecordLog(hr); + failureCount = RecordLog(failure->hr); break; case FailureType::FailFast: - failureCount = RecordFailFast(hr); + failureCount = RecordFailFast(failure->hr); break; }; failure->type = type; - failure->hr = hr; + failure->flags = FailureFlags::None; + 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(); @@ -3264,6 +3541,12 @@ namespace wil ::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) { @@ -3277,8 +3560,9 @@ namespace wil } // If the hook is enabled then it will be given the opportunity to call RoOriginateError to greatly improve the diagnostic experience - // for uncaught exceptions. - if (details::g_pfnOriginateCallback) + // 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); } @@ -3288,9 +3572,10 @@ namespace wil // 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; + 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 @@ -3328,13 +3613,13 @@ namespace wil } } - if (g_fBreakOnFailure && (g_pfnDebugBreak != nullptr)) + if ((WI_IsFlagSet(failure->flags, FailureFlags::RequestDebugBreak) || g_fBreakOnFailure) && (g_pfnDebugBreak != nullptr)) { g_pfnDebugBreak(); } } - inline __declspec(noreturn) void __stdcall WilFailFast(const wil::FailureInfo& failure) + inline RESULT_NORETURN void __stdcall WilFailFast(const wil::FailureInfo& failure) { if (g_pfnWilFailFast) { @@ -3346,13 +3631,19 @@ namespace wil RESULT_RAISE_FAST_FAIL_EXCEPTION; #endif + // Before we fail fast in this method, give the [optional] RoFailFastWithErrorContext a try. + if (g_pfnFailfastWithContextCallback) + { + g_pfnFailfastWithContextCallback(failure); + } + // parameter 0 is the !analyze code (FAST_FAIL_FATAL_APP_EXIT) EXCEPTION_RECORD er{}; er.NumberParameters = 1; // default to be safe, see below er.ExceptionCode = static_cast(STATUS_STACK_BUFFER_OVERRUN); // 0xC0000409 er.ExceptionFlags = EXCEPTION_NONCONTINUABLE; er.ExceptionInformation[0] = FAST_FAIL_FATAL_APP_EXIT; // see winnt.h, generated from minkernel\published\base\ntrtl_x.w - if (failure.returnAddress == 0) // FailureInfo does not have _ReturnAddress, have RaiseFailFastException generate it + 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 @@ -3373,7 +3664,7 @@ namespace wil } template - inline __declspec(noinline) void ReportFailureBaseReturn(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + inline __declspec(noinline) void ReportFailure_Return(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) { bool needPlatformException = ((T == FailureType::Exception) && WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && @@ -3384,18 +3675,23 @@ namespace wil wchar_t debugString[2048]; char callContextString[1024]; - LogFailure(__R_FN_CALL_FULL, T, hr, message, needPlatformException, + LogFailure(__R_FN_CALL_FULL, T, resultPair, message, needPlatformException, debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + + if (WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) + { + WilFailFast(failure); + } } template - inline __declspec(noinline) void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + inline __declspec(noinline) void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) { - ReportFailureBaseReturn(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_Return(__R_FN_CALL_FULL, resultPair, message, options); } template - inline __declspec(noinline) RESULT_NORETURN void ReportFailureBaseNoReturn(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + 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) && @@ -3406,10 +3702,10 @@ namespace wil wchar_t debugString[2048]; char callContextString[1024]; - LogFailure(__R_FN_CALL_FULL, T, hr, message, needPlatformException, + LogFailure(__R_FN_CALL_FULL, T, resultPair, message, needPlatformException, debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); __WI_SUPPRESS_4127_S - if (T == FailureType::FailFast) + if ((T == FailureType::FailFast) || WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) { WilFailFast(const_cast(failure)); } @@ -3434,52 +3730,52 @@ __WI_SUPPRESS_4127_E } template<> - inline __declspec(noinline) RESULT_NORETURN void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) { - ReportFailureBaseNoReturn(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_NoReturn(__R_FN_CALL_FULL, resultPair, message, options); } template<> - inline __declspec(noinline) RESULT_NORETURN void ReportFailure(__R_FN_PARAMS_FULL, HRESULT hr, PCWSTR message, ReportFailureOptions options) + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) { - ReportFailureBaseNoReturn(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_NoReturn(__R_FN_CALL_FULL, resultPair, message, options); } - __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr, _In_opt_ PCWSTR message, ReportFailureOptions 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(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); break; case FailureType::FailFast: - ReportFailure(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); break; case FailureType::Log: - ReportFailure(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); break; case FailureType::Return: - ReportFailure(__R_FN_CALL_FULL, hr, message, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); break; } } template - inline HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + 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); - HRESULT hr = S_OK; + ResultStatus resultPair; if (details::g_pfnResultFromCaughtExceptionInternal) { - hr = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + resultPair = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); } - const bool known = (FAILED(hr)); + const bool known = (FAILED(resultPair.hr)); if (!known) { - hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + resultPair = ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); } ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; @@ -3494,32 +3790,32 @@ __WI_SUPPRESS_4127_E // 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(__R_FN_CALL_FULL, hr, debugString, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); } else { - ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); } - return hr; + return resultPair; } template - inline HRESULT RESULT_NORETURN ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + 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); - HRESULT hr = S_OK; + ResultStatus resultPair; if (details::g_pfnResultFromCaughtExceptionInternal) { - hr = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + resultPair = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); } - const bool known = (FAILED(hr)); + const bool known = (FAILED(resultPair.hr)); if (!known) { - hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + resultPair = ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); } ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; @@ -3534,48 +3830,50 @@ __WI_SUPPRESS_4127_E // 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(__R_FN_CALL_FULL, hr, debugString, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); } else { - ReportFailure(__R_FN_CALL_FULL, hr, debugString, options); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); } + + RESULT_NORETURN_RESULT(resultPair); } template<> - inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + inline RESULT_NORETURN ResultStatus ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) { - ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported)); } template<> - inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + inline RESULT_NORETURN ResultStatus ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) { - ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported)); } template - inline void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + 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(__R_FN_CALL_FULL, hr, message); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); } template<> - inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + 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(__R_FN_CALL_FULL, hr, message); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); } template<> - inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + 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(__R_FN_CALL_FULL, hr, message); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); } template @@ -3583,25 +3881,25 @@ __WI_SUPPRESS_4127_E { va_list argList; va_start(argList, formatString); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); } template __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) { - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); } template<> __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) { - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); } template<> __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) { - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); } __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr) @@ -3629,17 +3927,18 @@ __WI_SUPPRESS_4127_E __declspec(noinline) inline HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) { const auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure(__R_FN_CALL_FULL, hr); + 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(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); } template<> @@ -3648,7 +3947,8 @@ __WI_SUPPRESS_4127_E __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) { const auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); } template @@ -3656,7 +3956,8 @@ __WI_SUPPRESS_4127_E { const auto err = GetLastErrorFail(__R_FN_CALL_FULL); const auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + ::SetLastError(err); return err; } @@ -3665,7 +3966,8 @@ __WI_SUPPRESS_4127_E { const auto err = GetLastErrorFail(__R_FN_CALL_FULL); const auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(err); } template<> @@ -3673,7 +3975,8 @@ __WI_SUPPRESS_4127_E { const auto err = GetLastErrorFail(__R_FN_CALL_FULL); const auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(err); } template @@ -3682,7 +3985,7 @@ __WI_SUPPRESS_4127_E __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) { const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); return hr; } @@ -3692,7 +3995,8 @@ __WI_SUPPRESS_4127_E __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) { const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); } template<> @@ -3701,7 +4005,8 @@ __WI_SUPPRESS_4127_E __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) { const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); - ReportFailure(__R_FN_CALL_FULL, hr); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); } template @@ -3709,9 +4014,9 @@ __WI_SUPPRESS_4127_E _Translates_NTSTATUS_to_HRESULT_(status) __declspec(noinline) inline HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) { - const auto hr = wil::details::NtStatusToHr(status); - ReportFailure(__R_FN_CALL_FULL, hr); - return hr; + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + return resultPair.hr; } template<> @@ -3719,8 +4024,9 @@ __WI_SUPPRESS_4127_E _Translates_NTSTATUS_to_HRESULT_(status) __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) { - const auto hr = wil::details::NtStatusToHr(status); - ReportFailure(__R_FN_CALL_FULL, hr); + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + RESULT_NORETURN_RESULT(resultPair.hr); } template<> @@ -3728,8 +4034,9 @@ __WI_SUPPRESS_4127_E _Translates_NTSTATUS_to_HRESULT_(status) __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) { - const auto hr = wil::details::NtStatusToHr(status); - ReportFailure(__R_FN_CALL_FULL, hr); + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + RESULT_NORETURN_RESULT(resultPair.hr); } template @@ -3737,7 +4044,7 @@ __WI_SUPPRESS_4127_E { wchar_t message[2048]; message[0] = L'\0'; - return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).hr; } template<> @@ -3745,7 +4052,7 @@ __WI_SUPPRESS_4127_E { wchar_t message[2048]; message[0] = L'\0'; - ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).hr); } template<> @@ -3753,25 +4060,25 @@ __WI_SUPPRESS_4127_E { wchar_t message[2048]; message[0] = L'\0'; - ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported); + 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, hr, formatString, 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, hr, formatString, 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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); } template @@ -3780,7 +4087,7 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); return hr; } @@ -3790,7 +4097,8 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); } template<> @@ -3799,7 +4107,8 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); } template @@ -3807,7 +4116,8 @@ __WI_SUPPRESS_4127_E { auto err = GetLastErrorFail(__R_FN_CALL_FULL); auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + ::SetLastError(err); return err; } @@ -3816,7 +4126,8 @@ __WI_SUPPRESS_4127_E { auto err = GetLastErrorFail(__R_FN_CALL_FULL); auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(err); } template<> @@ -3824,7 +4135,8 @@ __WI_SUPPRESS_4127_E { auto err = GetLastErrorFail(__R_FN_CALL_FULL); auto hr = __HRESULT_FROM_WIN32(err); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(err); } template @@ -3833,7 +4145,7 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); return hr; } @@ -3843,7 +4155,8 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); } template<> @@ -3852,7 +4165,8 @@ __WI_SUPPRESS_4127_E __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, hr, formatString, argList); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); } template @@ -3860,9 +4174,9 @@ __WI_SUPPRESS_4127_E _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) { - auto hr = wil::details::NtStatusToHr(status); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); - return hr; + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + return resultPair.hr; } template<> @@ -3870,8 +4184,9 @@ __WI_SUPPRESS_4127_E _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) { - auto hr = wil::details::NtStatusToHr(status); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + RESULT_NORETURN_RESULT(resultPair.hr); } template<> @@ -3879,8 +4194,9 @@ __WI_SUPPRESS_4127_E _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) { - auto hr = wil::details::NtStatusToHr(status); - ReportFailure_Msg(__R_FN_CALL_FULL, hr, formatString, argList); + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + RESULT_NORETURN_RESULT(resultPair.hr); } template @@ -3890,7 +4206,7 @@ __WI_SUPPRESS_4127_E 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); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr; } template<> @@ -3900,7 +4216,7 @@ __WI_SUPPRESS_4127_E wchar_t message[2048]; PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); StringCchCatW(message, ARRAYSIZE(message), L" -- "); - ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr); } template<> @@ -3910,7 +4226,7 @@ __WI_SUPPRESS_4127_E wchar_t message[2048]; PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); StringCchCatW(message, ARRAYSIZE(message), L" -- "); - ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr); } @@ -3943,7 +4259,7 @@ __WI_SUPPRESS_4127_E #endif template - __declspec(noreturn) inline void ReportFailure_CustomExceptionHelper(_Inout_ T &exception, __R_FN_PARAMS_FULL, _In_opt_ PCWSTR message = nullptr) + 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. @@ -3954,9 +4270,14 @@ __WI_SUPPRESS_4127_E wchar_t debugString[2048]; char callContextString[1024]; - LogFailure(__R_FN_CALL_FULL, FailureType::Exception, hr, message, false, // false = does not need debug string + LogFailure(__R_FN_CALL_FULL, FailureType::Exception, ResultStatus::FromResult(hr), message, false, // false = does not need debug string debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + if (WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) + { + WilFailFast(failure); + } + // push the failure info context into the custom exception class SetFailureInfo(failure, exception); @@ -4147,8 +4468,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HRESULT, Log_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) { if (FAILED(hr)) { @@ -4184,8 +4506,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(BOOL, Log_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) { if (!ret) { @@ -4194,8 +4517,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(DWORD, Log_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) { if (FAILED_WIN32(err)) { @@ -4204,8 +4528,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) { if (handle == INVALID_HANDLE_VALUE) { @@ -4214,8 +4539,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) { if (handle == nullptr) { @@ -4244,8 +4570,9 @@ __WI_SUPPRESS_4127_E } } + // 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) { if (condition) { @@ -4254,8 +4581,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) { if (!condition) { @@ -4284,8 +4612,9 @@ __WI_SUPPRESS_4127_E } } + // 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) { if (condition) { @@ -4294,8 +4623,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) { if (!condition) { @@ -4324,8 +4654,9 @@ __WI_SUPPRESS_4127_E } } + // 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) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(NTSTATUS, Log_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) { if (FAILED_NTSTATUS(status)) { @@ -4693,8 +5024,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_METHOD(DWORD, FailFast_IfWin32Error)(__RFF_CONDITIONAL_FN_PARAMS DWORD err) { if (FAILED_WIN32(err)) { @@ -4723,9 +5055,10 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -4734,8 +5067,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) { if (pointer == nullptr) { @@ -4763,9 +5097,10 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -4774,8 +5109,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -4803,9 +5139,10 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -4814,8 +5151,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5129,10 +5467,11 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5141,9 +5480,10 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5257,9 +5597,10 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFastImmediate_IfNull)(_Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5268,8 +5609,9 @@ __WI_SUPPRESS_4127_E 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) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5803,7 +6145,7 @@ __WI_SUPPRESS_4127_E // Intentionally removed logging from this policy as logging is more useful at the caller. struct err_returncode_policy { - typedef HRESULT result; + 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; } diff --git a/Externals/WIL/include/wil/result_originate.h b/Externals/WIL/include/wil/result_originate.h index 328a817778..15b92354ec 100644 --- a/Externals/WIL/include/wil/result_originate.h +++ b/Externals/WIL/include/wil/result_originate.h @@ -31,7 +31,6 @@ #include "com.h" #include -#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them. namespace wil { namespace details @@ -82,6 +81,36 @@ namespace wil } } } + + // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT + // matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will + // result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and + // the calling method fails fast the same way it always has. + inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT + { + wil::com_ptr_nothrow restrictedErrorInformation; + if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) + { + wil::unique_bstr descriptionUnused; + HRESULT existingHr = failure.hr; + wil::unique_bstr restrictedDescriptionUnused; + wil::unique_bstr capabilitySidUnused; + if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) && + (existingHr == failure.hr)) + { + // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext + // so we must restore it via SetRestrictedErrorInfo first. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + RoFailFastWithErrorContext(existingHr); + } + else + { + // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast + // in this method, so it is available in the debugger just-in-case. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + } + } + } } // namespace details } // namespace wil @@ -89,8 +118,8 @@ namespace wil WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] { ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); + ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); return 1; -}); -#endif // __cplusplus_winrt +}) #endif // __WIL_RESULT_ORIGINATE_INCLUDED diff --git a/Externals/WIL/include/wil/safecast.h b/Externals/WIL/include/wil/safecast.h index 8d477698ed..d443f99d49 100644 --- a/Externals/WIL/include/wil/safecast.h +++ b/Externals/WIL/include/wil/safecast.h @@ -121,76 +121,76 @@ namespace wil // Mappings of all conversions defined in intsafe.h to intsafe_conversion // Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve // to the base types. The base types are used since they do not vary based on architecture. - template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar; - template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt; - template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong; - template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort; - template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8; - template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong; - template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar; - template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt; - template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong; - template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort; - template<> constexpr auto intsafe_conversion = IntToChar; - template<> constexpr auto intsafe_conversion = IntToShort; - template<> constexpr auto intsafe_conversion = IntToInt8; - template<> constexpr auto intsafe_conversion = IntToULongLong; - template<> constexpr auto intsafe_conversion = IntToUChar; - template<> constexpr auto intsafe_conversion = IntToUInt; - template<> constexpr auto intsafe_conversion = IntToULong; - template<> constexpr auto intsafe_conversion = IntToUShort; - template<> constexpr auto intsafe_conversion = LongToChar; - template<> constexpr auto intsafe_conversion = LongToInt; - template<> constexpr auto intsafe_conversion = LongToShort; - template<> constexpr auto intsafe_conversion = LongToInt8; - template<> constexpr auto intsafe_conversion = LongToULongLong; - template<> constexpr auto intsafe_conversion = LongToUChar; - template<> constexpr auto intsafe_conversion = LongToUInt; - template<> constexpr auto intsafe_conversion = LongToULong; - template<> constexpr auto intsafe_conversion = LongToUShort; - template<> constexpr auto intsafe_conversion = ShortToChar; - template<> constexpr auto intsafe_conversion = ShortToInt8; - template<> constexpr auto intsafe_conversion = ShortToULongLong; - template<> constexpr auto intsafe_conversion = ShortToUChar; - template<> constexpr auto intsafe_conversion = ShortToUInt; - template<> constexpr auto intsafe_conversion = ShortToULong; - template<> constexpr auto intsafe_conversion = ShortToUShort; - template<> constexpr auto intsafe_conversion = Int8ToULongLong; - template<> constexpr auto intsafe_conversion = Int8ToUChar; - template<> constexpr auto intsafe_conversion = Int8ToUInt; - template<> constexpr auto intsafe_conversion = Int8ToULong; - template<> constexpr auto intsafe_conversion = Int8ToUShort; - template<> constexpr auto intsafe_conversion = ULongLongToLongLong; - template<> constexpr auto intsafe_conversion = ULongLongToChar; - template<> constexpr auto intsafe_conversion = ULongLongToInt; - template<> constexpr auto intsafe_conversion = ULongLongToLong; - template<> constexpr auto intsafe_conversion = ULongLongToShort; - template<> constexpr auto intsafe_conversion = ULongLongToInt8; - template<> constexpr auto intsafe_conversion = ULongLongToUChar; - template<> constexpr auto intsafe_conversion = ULongLongToUInt; - template<> constexpr auto intsafe_conversion = ULongLongToULong; - template<> constexpr auto intsafe_conversion = ULongLongToUShort; - template<> constexpr auto intsafe_conversion = UInt8ToChar; - template<> constexpr auto intsafe_conversion = UIntToInt8; - template<> constexpr auto intsafe_conversion = UIntToChar; - template<> constexpr auto intsafe_conversion = UIntToInt; - template<> constexpr auto intsafe_conversion = UIntToLong; - template<> constexpr auto intsafe_conversion = UIntToShort; - template<> constexpr auto intsafe_conversion = UIntToInt8; - template<> constexpr auto intsafe_conversion = UIntToUChar; - template<> constexpr auto intsafe_conversion = UIntToUShort; - template<> constexpr auto intsafe_conversion = ULongToChar; - template<> constexpr auto intsafe_conversion = ULongToInt; - template<> constexpr auto intsafe_conversion = ULongToLong; - template<> constexpr auto intsafe_conversion = ULongToShort; - template<> constexpr auto intsafe_conversion = ULongToInt8; - template<> constexpr auto intsafe_conversion = ULongToUChar; - template<> constexpr auto intsafe_conversion = ULongToUInt; - template<> constexpr auto intsafe_conversion = ULongToUShort; - template<> constexpr auto intsafe_conversion = UShortToChar; - template<> constexpr auto intsafe_conversion = UShortToShort; - template<> constexpr auto intsafe_conversion = UShortToInt8; - template<> constexpr auto intsafe_conversion = UShortToUChar; + 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; } // Unsafe conversion where failure results in fail fast. diff --git a/Externals/WIL/include/wil/stl.h b/Externals/WIL/include/wil/stl.h index 56e95f4544..3f45eba2b2 100644 --- a/Externals/WIL/include/wil/stl.h +++ b/Externals/WIL/include/wil/stl.h @@ -15,19 +15,17 @@ #include "resource.h" #include #include +#include +#include +#if _HAS_CXX17 +#include +#endif + +#ifndef WI_STL_FAIL_FAST_IF +#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF +#endif #if defined(WIL_ENABLE_EXCEPTIONS) -namespace std -{ - template - class vector; - - template - struct char_traits; - - template - class basic_string; -} // namespace std namespace wil { @@ -42,7 +40,7 @@ namespace wil template struct rebind { - typedef secure_allocator other; + using other = secure_allocator; }; secure_allocator() @@ -100,6 +98,8 @@ namespace wil 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(); } @@ -117,6 +117,78 @@ namespace wil 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. + constexpr const TChar& operator[](size_type idx) const noexcept + { + WI_ASSERT(idx <= this->size() && this->data() != nullptr); + return this->data()[idx]; + } + + 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; +#endif // _HAS_CXX17 + } // namespace wil #endif // WIL_ENABLE_EXCEPTIONS diff --git a/Externals/WIL/include/wil/token_helpers.h b/Externals/WIL/include/wil/token_helpers.h index 746a13ef7a..aec3952b2d 100644 --- a/Externals/WIL/include/wil/token_helpers.h +++ b/Externals/WIL/include/wil/token_helpers.h @@ -21,7 +21,9 @@ #include // for GetUserNameEx() +#ifndef SECURITY_WIN32 #define SECURITY_WIN32 +#endif #include namespace wil @@ -34,25 +36,25 @@ namespace wil // be an info class value that uses the same structure. That is the case for the file // system information. template struct MapTokenStructToInfoClass; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; }; + 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 const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; }; - template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; }; + 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; }; } /// @endcond @@ -85,13 +87,12 @@ namespace wil or privilege-adjustment are examples of uses. ~~~~ wil::unique_handle callerToken; - RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true)); + 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 asSelf When true, and if the thread is impersonating, the thread token is opened using the - process token's rights. + @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) { @@ -122,6 +123,7 @@ namespace wil } #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! @@ -287,11 +289,12 @@ namespace wil return tokenInfo; } #endif +#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8 /// @cond namespace details { - inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken) + inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken) { FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken)); @@ -524,6 +527,7 @@ namespace wil 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. @@ -573,6 +577,7 @@ namespace wil 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) diff --git a/Externals/WIL/include/wil/traceloggingconfig.h b/Externals/WIL/include/wil/traceloggingconfig.h new file mode 100644 index 0000000000..e252d41da5 --- /dev/null +++ b/Externals/WIL/include/wil/traceloggingconfig.h @@ -0,0 +1,71 @@ +#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. +// +//********************************************************* + +#ifndef __WIL_TRACELOGGING_CONFIG_H +#define __WIL_TRACELOGGING_CONFIG_H + +// 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/Externals/WIL/include/wil/win32_helpers.h b/Externals/WIL/include/wil/win32_helpers.h index 0184cf2acc..12fd5bcb57 100644 --- a/Externals/WIL/include/wil/win32_helpers.h +++ b/Externals/WIL/include/wil/win32_helpers.h @@ -15,33 +15,177 @@ #include // GetSystemTimeAsFileTime #include // GetProcAddress #include // GetModuleFileNameExW (macro), K32GetModuleFileNameExW -#include +#include #include +// detect std::bit_cast +#ifdef __has_include +# if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include() +# include +# endif +#endif + +#if __cpp_lib_bit_cast >= 201806L +# define __WI_CONSTEXPR_BIT_CAST constexpr +#else +# define __WI_CONSTEXPR_BIT_CAST inline +#endif + #include "result.h" #include "resource.h" #include "wistd_functional.h" #include "wistd_type_traits.h" +#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 + +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 wil { //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT. //! CDFs has a limit of 254. - size_t const max_path_segment_length = 255; + constexpr size_t max_path_segment_length = 255; //! Character length not including the null, MAX_PATH (260) includes the null. - size_t const max_path_length = 259; + 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. - size_t const max_extended_path_length = 0x7FFF - 24; + constexpr size_t max_extended_path_length = 0x7FFF - 24; //! For {guid} string form. Includes space for the null terminator. - size_t const guid_string_buffer_length = 39; + constexpr size_t guid_string_buffer_length = 39; //! For {guid} string form. Not including the null terminator. - size_t const guid_string_length = 38; + 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) + + 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); + } + } + + [[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. @@ -56,36 +200,126 @@ namespace wil namespace filetime { - inline unsigned long long to_int64(const FILETIME &ft) + 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 } - inline FILETIME from_int64(unsigned long long i64) + __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 } - inline FILETIME add(_In_ FILETIME const &ft, long long delta) + __WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT { - return from_int64(to_int64(ft) + delta); + return from_int64(to_int64(ft) + delta100ns); } - inline bool is_empty(const FILETIME &ft) + constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT { return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0); } - inline FILETIME get_system_time() + 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) + /// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation. + /// + /// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation. + /// + /// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation. + /// For example + /// + /// start = GetTickCount64(); + /// hibernate(); + /// ...wake from hibernation 30 minutes later...; + /// elapsed = GetTickCount64() - start; + /// // elapsed = 30min + /// + /// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so). + /// + /// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter). + /// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx + inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT + { + ULONGLONG now{}; + QueryUnbiasedInterruptTime(&now); + return now; + } + + /// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation. + /// @see QueryUnbiasedInterruptTimeAs100ns + inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT + { + return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns()); + } +#endif // _APISETREALTIME_ + } +#pragma endregion + +#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 @@ -95,7 +329,7 @@ namespace wil // 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) + HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function callback) WI_NOEXCEPT { details::string_maker maker; @@ -112,14 +346,24 @@ namespace wil else { // Did not fit in the stack allocated buffer, need to do 2 phase construction. - // valueLengthNeededWithNull includes the null so subtract that as make() will add space for it. - RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1)); + // 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)); - size_t secondLength{}; - RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength)); + RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull)); + WI_ASSERT(valueLengthNeededWithNull > 0); - // Ensure callback produces consistent result. - FAIL_FAST_IF(valueLengthNeededWithNull != secondLength); + // 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; @@ -167,9 +411,6 @@ namespace wil }); } - // This function does not work beyond the default stack buffer size (255). - // Needs to to retry in a loop similar to wil::GetModuleFileNameExW - // These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3 template HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT { @@ -179,8 +420,9 @@ namespace wil DWORD lengthToUse = static_cast(valueLength); BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse); RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)); - // On both success or insufficient buffer case, add +1 for the null-terminating character - *valueLengthNeededWithNul = lengthToUse + 1; + + // On success, return the amount used; on failure, try doubling + *valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2); return S_OK; }); } @@ -201,13 +443,11 @@ namespace wil } #endif - /** Looks up the environment variable 'key' and fails if it is not found. - 'key' should not have '%' prefix and suffix. - Dangerous since environment variable generally are optional. */ - template + /** 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, + 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 @@ -232,12 +472,11 @@ namespace wil }); } - /** Looks up the environment variable 'key' and returns null if it is not found. - 'key' should not have '%' prefix and suffix. */ - template + /** 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); + const auto hr = wil::GetEnvironmentVariableW(key, result); RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND))); return S_OK; } @@ -245,68 +484,44 @@ namespace wil /** 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) + HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT { - // initialBufferLength is a template parameter to allow for testing. It creates some waste for - // shorter paths, but avoids iteration through the loop in common cases where paths are less - // than 128 characters. - // wil::max_extended_path_length + 1 (for the null char) - // + 1 (to be certain GetModuleFileNameExW didn't truncate) - size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0; - size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation; - - details::string_maker maker; - - for (size_t lengthWithNull = initialBufferLength; - lengthWithNull <= maxExtendedPathLengthWithNull; - lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull)) + auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT { - // make() adds space for the trailing null - RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1)); - 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. - copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast(lengthWithNull)); + // The only way to be sure it didn't truncate is if it didn't need the whole buffer. The + // count copied to the buffer includes the nul-character as well. + copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast(valueLength)); + valueUsedWithNul = copiedCount + 1; copyFailed = (0 == copiedCount); - copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1); + 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. - copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast(lengthWithNull)); + // and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include + // the nul-character + copiedCount = ::GetModuleFileNameW(module, value, static_cast(valueLength)); + valueUsedWithNul = copiedCount + 1; copyFailed = (0 == copiedCount); - copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull); + copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength); } - if (copyFailed) - { - RETURN_LAST_ERROR(); - } - else if (copySucceededWithNoTruncation) - { - path = maker.release(); - return S_OK; - } + RETURN_LAST_ERROR_IF(copyFailed); - WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)); + // When the copy truncated, request another try with more space. + *valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2); - if (lengthWithNull == maxExtendedPathLengthWithNull) - { - // If we've reached this point, there's no point in trying a larger buffer size. - break; - } - } + return S_OK; + }; - // Any path should fit into the maximum max_extended_path_length. If we reached here, something went - // terribly wrong. - FAIL_FAST(); + return wil::AdaptFixedSizeToAllocatedResult(path, wistd::move(adapter)); } /** Retrieves the fully qualified path for the file that contains the specified module. @@ -314,7 +529,7 @@ namespace wil 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) + HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT { return wil::GetModuleFileNameExW(nullptr, module, path); } @@ -357,50 +572,134 @@ namespace wil } #endif - /** Looks up the environment variable 'key' and fails if it is not found. - 'key' should not have '%' prefix and suffix. - Dangerous since environment variable generally are optional. */ - template + /** 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)); + THROW_IF_FAILED((wil::GetEnvironmentVariableW(key, result))); return result; } - /** Looks up the environment variable 'key' and returns null if it is not found. - 'key' should not have '%' prefix and suffix. */ - template + /** 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)); + THROW_IF_FAILED((wil::TryGetEnvironmentVariableW(key, result))); return result; } - template - string_type GetModuleFileNameW(HMODULE module) + template + string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */) { string_type result; - THROW_IF_FAILED(wil::GetModuleFileNameW(module, result)); + THROW_IF_FAILED((wil::GetModuleFileNameW(module, result))); return result; } - template + template string_type GetModuleFileNameExW(HANDLE process, HMODULE module) { string_type result; - THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result)); + THROW_IF_FAILED((wil::GetModuleFileNameExW(process, module, 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() { return reinterpret_cast(&__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 @@ -410,19 +709,19 @@ namespace wil INIT_ONCE& m_once; unsigned long m_flags = INIT_ONCE_INIT_FAILED; public: - init_once_completer(_In_ INIT_ONCE& once) : m_once(once) + 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() + void success() WI_NOEXCEPT { m_flags = 0; } #pragma warning(pop) - ~init_once_completer() + ~init_once_completer() WI_NOEXCEPT { ::InitOnceComplete(&m_once, m_flags, nullptr); } diff --git a/Externals/WIL/include/wil/win32_result_macros.h b/Externals/WIL/include/wil/win32_result_macros.h new file mode 100644 index 0000000000..947db33260 --- /dev/null +++ b/Externals/WIL/include/wil/win32_result_macros.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. +// +//********************************************************* +#ifndef __WIL_WIN32_RESULTMACROS_INCLUDED +#define __WIL_WIN32_RESULTMACROS_INCLUDED + +#include "result_macros.h" + +// Helpers for return macros +#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; +} + +//***************************************************************************** +// 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()); + } + + 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)); + } + } +} + +#endif // __WIL_WIN32_RESULTMACROS_INCLUDED diff --git a/Externals/WIL/include/wil/winrt.h b/Externals/WIL/include/wil/winrt.h index c2456b27a5..3ce91019ce 100644 --- a/Externals/WIL/include/wil/winrt.h +++ b/Externals/WIL/include/wil/winrt.h @@ -19,24 +19,40 @@ #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 -#ifdef WIL_ENABLE_EXCEPTIONS /// @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 - class basic_string; - template struct less; } -/// @endcond +#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 @@ -50,10 +66,9 @@ namespace std namespace wil { -#ifdef _INC_TIME // time_t is the number of 1 - second intervals since January 1, 1970. - long long const SecondsToStartOf1970 = 0x2b6109100; - long long const HundredNanoSecondsInSecond = 10000000LL; + constexpr long long SecondsToStartOf1970 = 0x2b6109100; + constexpr long long HundredNanoSecondsInSecond = 10000000LL; inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime) { @@ -67,7 +82,6 @@ namespace wil dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond; return dateTime; } -#endif // _INC_TIME #pragma region HSTRING Helpers /// @cond @@ -145,16 +159,17 @@ namespace wil return str; } -#ifdef WIL_ENABLE_EXCEPTIONS - template - static const wchar_t* get_buffer( - const std::basic_string& str, - UINT32* length) WI_NOEXCEPT + // Overload for std::wstring, or at least things that behave like std::wstring, without adding a dependency + // on STL headers + template + static wistd::enable_if_t().c_str())>, + wistd::is_same().length())>>, + const wchar_t*> get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT { *length = static_cast(str.length()); return str.c_str(); } -#endif template static auto compare(LhsT&& lhs, RhsT&& rhs) -> @@ -224,8 +239,8 @@ namespace wil //! Detects if one or more embedded null is present in an HSTRING. inline bool HasEmbeddedNull(_In_opt_ HSTRING value) { - BOOL hasEmbeddedNull; - WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull); + BOOL hasEmbeddedNull = FALSE; + (void)WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull); return hasEmbeddedNull != FALSE; } @@ -429,14 +444,14 @@ namespace wil #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 struct type // T holder { - type() {}; - type(T&& value) : m_value(wistd::forward(value)) {}; + type() = default; + type(T&& value) : m_value(wistd::forward(value)) {} operator T() const { return m_value; } type& operator=(T&& value) { m_value = wistd::forward(value); return *this; } T Get() const { return m_value; } // Returning T&& to support move only types - // In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen + // In case of absence of T::operator=(T&&) a call to T::operator=(const T&) will happen T&& Get() { return wistd::move(m_value); } HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; } @@ -730,7 +745,7 @@ namespace wil vector_range_nothrow(const vector_range_nothrow&) = delete; vector_range_nothrow& operator=(const vector_range_nothrow&) = delete; - vector_range_nothrow(vector_range_nothrow&& other) : + 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)) { @@ -985,6 +1000,39 @@ namespace wil }; #pragma endregion +#if defined(__WI_HAS_STD_VECTOR) + /** 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. + ~~~ + ComPtr> values = GetValues(); + std::vector> allData = wil::to_vector(values); + for (ComPtr const& item : allData) + { + // use item + } + 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<> template class iterable_range_nothrow @@ -998,7 +1046,7 @@ namespace wil iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete; iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete; - iterable_range_nothrow(iterable_range_nothrow&& other) : + 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) { @@ -1274,9 +1322,9 @@ namespace details // LastType::type boolValue; template struct LastType { - template struct LastTypeOfTs + template struct LastTypeOfTs { - typedef typename LastTypeOfTs::type type; + typedef typename LastTypeOfTs::type type; }; template struct LastTypeOfTs @@ -1284,8 +1332,8 @@ namespace details typedef T type; }; - template - static typename LastTypeOfTs::type LastTypeOfTsFunc() {} + template + static typename LastTypeOfTs::type LastTypeOfTsFunc() {} typedef decltype(LastTypeOfTsFunc()) type; }; @@ -1315,14 +1363,28 @@ namespace details typedef wistd::remove_pointer_t TIDelegate; auto callback = Callback, TIDelegate, TBaseAgility>>( - [func = wistd::forward(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT + [func = wistd::forward(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT { HRESULT hr = S_OK; - if (status != AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully + if (status != ABI::Windows::Foundation::AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully { - ComPtr asyncInfo; - operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // All must implement IAsyncInfo - asyncInfo->get_ErrorCode(&hr); + // 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); @@ -1340,21 +1402,35 @@ namespace details typedef wistd::remove_pointer_t TIDelegate; auto callback = Callback, TIDelegate, TBaseAgility>>( - [func = wistd::forward(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT + [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; - if (status == AsyncStatus::Completed) + // avoid a potentially costly marshaled QI / call if we completed successfully + if (status == ABI::Windows::Foundation::AsyncStatus::Completed) { hr = operation->GetResults(result.GetAddressOf()); } else { - // avoid a potentially costly marshaled QI / call if we completed successfully - ComPtr asyncInfo; - operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this - asyncInfo->get_ErrorCode(&hr); + // 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()); @@ -1378,7 +1454,7 @@ namespace details RETURN_HR(m_completedEventHandle.create()); } - HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, AsyncStatus status) override + HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, ABI::Windows::Foundation::AsyncStatus status) override { m_status = status; m_completedEventHandle.SetEvent(); @@ -1390,13 +1466,13 @@ namespace details return m_completedEventHandle.get(); } - AsyncStatus GetStatus() const + ABI::Windows::Foundation::AsyncStatus GetStatus() const { return m_status; } private: - volatile AsyncStatus m_status = AsyncStatus::Started; + volatile ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started; wil::unique_event_nothrow m_completedEventHandle; }; @@ -1419,12 +1495,25 @@ namespace details } RETURN_IF_FAILED(hr); - if (completedDelegate->GetStatus() != AsyncStatus::Completed) + if (completedDelegate->GetStatus() != ABI::Windows::Foundation::AsyncStatus::Completed) { - Microsoft::WRL::ComPtr asyncInfo; - operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this - hr = E_UNEXPECTED; - asyncInfo->get_ErrorCode(&hr); // error return ignored, ok? + // 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; @@ -1669,7 +1758,7 @@ namespace details IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler* competed) override { - competed->Invoke(this, AsyncStatus::Completed); + competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); return S_OK; } @@ -1708,7 +1797,7 @@ namespace details public: IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override { - competed->Invoke(this, AsyncStatus::Completed); + competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); return S_OK; } @@ -1866,7 +1955,19 @@ public: } else { - auto resolvedSender = m_weakSender.Resolve(); + 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); @@ -2205,7 +2306,7 @@ struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler // For size_t and other necessary types +#include // For size_t and other necessary types /// @cond #if defined(_MSC_VER) && !defined(__clang__) @@ -107,6 +107,29 @@ # 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 severly underrepresented in libc++ and checks such as // __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we // instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls @@ -448,7 +471,7 @@ namespace wistd // ("Windows Implementation" std) { - typedef decltype(__nullptr) nullptr_t; + using nullptr_t = decltype(__nullptr); template struct __less @@ -531,18 +554,18 @@ namespace wistd // ("Windows Implementation" std) template struct __WI_LIBCPP_TEMPLATE_VIS unary_function { - typedef _Arg argument_type; - typedef _Result result_type; + using argument_type = _Arg; + using result_type = _Result; }; template struct __WI_LIBCPP_TEMPLATE_VIS binary_function { - typedef _Arg1 first_argument_type; - typedef _Arg2 second_argument_type; - typedef _Result result_type; + using first_argument_type = _Arg1; + using second_argument_type = _Arg2; + using result_type = _Result; }; } /// @endcond -#endif _WISTD_CONFIG_H_ +#endif // _WISTD_CONFIG_H_ diff --git a/Externals/WIL/include/wil/wistd_functional.h b/Externals/WIL/include/wil/wistd_functional.h index f664385e64..7f86ee958f 100644 --- a/Externals/WIL/include/wil/wistd_functional.h +++ b/Externals/WIL/include/wil/wistd_functional.h @@ -46,6 +46,7 @@ #pragma warning(push) #pragma warning(disable: 4324) +#pragma warning(disable: 4800) /// @cond namespace wistd // ("Windows Implementation" std) @@ -249,25 +250,28 @@ namespace wistd // ("Windows Implementation" std) return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...); } - } // __function - - template - class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> - : public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>, - public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)> - { // 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects // that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12 // pointers (__base vtable takes an additional one). - static constexpr size_t __buffer_size = 13 * sizeof(void*); + constexpr const size_t __buffer_size = 13 * sizeof(void*); - typedef __function::__base<_Rp(_ArgTypes...)> __base; + } // __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<__buffer_size>::type __buf_; + typename aligned_storage<__function::__buffer_size>::type __buf_; __base* __f_; __WI_LIBCPP_NO_CFI static __base *__as_base(void *p) { - return reinterpret_cast<__base*>(p); + return static_cast<__base*>(p); } template @@ -281,7 +285,7 @@ namespace wistd // ("Windows Implementation" std) template struct __callable_imp<_Fp, false> { - static const bool value = false; + static constexpr bool value = false; }; template @@ -296,11 +300,12 @@ namespace wistd // ("Windows Implementation" std) template using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; public: - typedef _Rp result_type; + 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&); @@ -340,7 +345,7 @@ namespace wistd // ("Windows Implementation" std) __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS function<_Rp(_ArgTypes...)>::function(const function& __f) { - if (__f.__f_ == 0) + if (__f.__f_ == nullptr) __f_ = 0; else { @@ -353,7 +358,7 @@ namespace wistd // ("Windows Implementation" std) __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS function<_Rp(_ArgTypes...)>::function(function&& __f) { - if (__f.__f_ == 0) + if (__f.__f_ == nullptr) __f_ = 0; else { @@ -368,14 +373,14 @@ namespace wistd // ("Windows Implementation" std) template __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS function<_Rp(_ArgTypes...)>::function(_Fp __f) - : __f_(0) + : __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((void*)&__buf_) _FF(wistd::move(__f)); + __f_ = ::new(static_cast(&__buf_)) _FF(wistd::move(__f)); } } @@ -429,7 +434,7 @@ namespace wistd // ("Windows Implementation" std) typedef __function::__func::type, _Rp(_ArgTypes...)> _FF; static_assert(sizeof(_FF) <= sizeof(__buf_), "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); - __f_ = ::new((void*)&__buf_) _FF(wistd::move(__f)); + __f_ = ::new(static_cast(&__buf_)) _FF(wistd::move(__f)); } return *this; @@ -483,7 +488,7 @@ namespace wistd // ("Windows Implementation" std) _Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const { - if (__f_ == 0) + if (__f_ == nullptr) __throw_bad_function_call(); return (*__f_)(wistd::forward<_ArgTypes>(__arg)...); } diff --git a/Externals/WIL/include/wil/wistd_memory.h b/Externals/WIL/include/wil/wistd_memory.h index 48031c5760..0a5be28825 100644 --- a/Externals/WIL/include/wil/wistd_memory.h +++ b/Externals/WIL/include/wil/wistd_memory.h @@ -60,13 +60,13 @@ namespace wistd // ("Windows Implementation" std) template ::value> struct __pointer_type { - typedef typename _Dp::pointer type; + using type = typename _Dp::pointer; }; template struct __pointer_type<_Tp, _Dp, false> { - typedef _Tp* type; + using type = _Tp*; }; } // __pointer_type_imp @@ -74,16 +74,16 @@ namespace wistd // ("Windows Implementation" std) template struct __pointer_type { - typedef typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type 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 { - typedef _Tp _ParamT; - typedef _Tp& reference; - typedef const _Tp& const_reference; + 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_() {} @@ -115,10 +115,10 @@ namespace wistd // ("Windows Implementation" std) template struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp { - typedef _Tp _ParamT; - typedef _Tp& reference; - typedef const _Tp& const_reference; - typedef _Tp __value_type; + 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; @@ -149,10 +149,10 @@ namespace wistd // ("Windows Implementation" std) struct __second_tag {}; template - class __compressed_pair : private __compressed_pair_elem<_T1, 0>, - private __compressed_pair_elem<_T2, 1> { - typedef __compressed_pair_elem<_T1, 0> _Base1; - typedef __compressed_pair_elem<_T2, 1> _Base2; + 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. @@ -271,7 +271,7 @@ namespace wistd // ("Windows Implementation" std) __WI_LIBCPP_INLINE_VISIBILITY default_delete(const default_delete<_Up>&, typename enable_if::value>::type* = - 0) WI_NOEXCEPT {} + nullptr) WI_NOEXCEPT {} __WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT { static_assert(sizeof(_Tp) > 0, @@ -299,7 +299,7 @@ namespace wistd // ("Windows Implementation" std) template __WI_LIBCPP_INLINE_VISIBILITY default_delete(const default_delete<_Up[]>&, - typename _EnableIfConvertible<_Up>::type* = 0) WI_NOEXCEPT {} + typename _EnableIfConvertible<_Up>::type* = nullptr) WI_NOEXCEPT {} template __WI_LIBCPP_INLINE_VISIBILITY @@ -319,32 +319,32 @@ namespace wistd // ("Windows Implementation" std) template struct __unique_ptr_deleter_sfinae { static_assert(!is_reference<_Deleter>::value, "incorrect specialization"); - typedef const _Deleter& __lval_ref_type; - typedef _Deleter&& __good_rval_ref_type; - typedef true_type __enable_rval_overload; + 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&> { - typedef const _Deleter& __lval_ref_type; - typedef const _Deleter&& __bad_rval_ref_type; - typedef false_type __enable_rval_overload; + 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&> { - typedef _Deleter& __lval_ref_type; - typedef _Deleter&& __bad_rval_ref_type; - typedef false_type __enable_rval_overload; + 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: - typedef _Tp element_type; - typedef _Dp deleter_type; - typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + 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"); @@ -355,7 +355,7 @@ namespace wistd // ("Windows Implementation" std) struct __nat { int __for_bool_; }; #ifndef __WI_LIBCPP_CXX03_LANG - typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>; template using _LValRefType = @@ -582,9 +582,9 @@ namespace wistd // ("Windows Implementation" std) template class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> { public: - typedef _Tp element_type; - typedef _Dp deleter_type; - typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + using element_type = _Tp; + using deleter_type = _Dp; + using pointer = typename __pointer_type<_Tp, deleter_type>::type; private: __compressed_pair __ptr_; @@ -602,7 +602,7 @@ namespace wistd // ("Windows Implementation" std) {}; #ifndef __WI_LIBCPP_CXX03_LANG - typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>; template using _LValRefType = diff --git a/Externals/WIL/include/wil/wrl.h b/Externals/WIL/include/wil/wrl.h index e7f7c6c08c..6e95f1713f 100644 --- a/Externals/WIL/include/wil/wrl.h +++ b/Externals/WIL/include/wil/wrl.h @@ -14,6 +14,11 @@ #include #include "result.h" #include "common.h" // wistd type_traits helpers +#include // GetModuleHandleW + +/// @cond +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +/// @endcond namespace wil { @@ -79,6 +84,44 @@ namespace wil 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/Externals/WIL/natvis/wil.natvis b/Externals/WIL/natvis/wil.natvis new file mode 100644 index 0000000000..c42fdafec8 --- /dev/null +++ b/Externals/WIL/natvis/wil.natvis @@ -0,0 +1,114 @@ + + + + + {_Callee._Object} + + + + + + empty + {*_Impl} + + *_Impl + + + + + empty + {*_Myptr} + _Myptr + + _Myptr + + + + + empty + {_Myptr,su} + _Myptr + + _Myptr + wcslen(_Myptr) + + + + + empty + {_Myptr,s} + _Myptr + + _Myptr + strlen(_Myptr) + + + + + empty + {*m_ptr} + m_ptr + + m_ptr + + + + + empty + {m_ptr} + + + + + empty + {m_ptr} + + + + + empty + {m_ptr} + m_ptr + + + + + empty + {m_ptr,su} + m_ptr + + wcslen(m_ptr) + + + + empty + {m_ptr,s} + m_ptr + + strlen(m_ptr) + + + + + {m_ptr} + + m_ptr + + + + + + + {_Mydata,[_Mysize]} + + size() + + + \ No newline at end of file diff --git a/Externals/WIL/packaging/nuget/CMakeLists.txt b/Externals/WIL/packaging/nuget/CMakeLists.txt index f3fd938b22..8f5c65e1c5 100644 --- a/Externals/WIL/packaging/nuget/CMakeLists.txt +++ b/Externals/WIL/packaging/nuget/CMakeLists.txt @@ -7,6 +7,7 @@ file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_ file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe}) file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h) +file(GLOB_RECURSE wil_natvis ${CMAKE_SOURCE_DIR}/natvis/*.natvis) add_custom_command(OUTPUT ${wil_nupkg} COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive @@ -14,6 +15,7 @@ add_custom_command(OUTPUT ${wil_nupkg} ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets ${wil_headers} + ${wil_natvis} ${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt) diff --git a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec index be709b9ca0..885f8b0834 100644 --- a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec +++ b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.nuspec @@ -16,6 +16,7 @@ + \ No newline at end of file diff --git a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets index 29d756a9f9..77d21ca684 100644 --- a/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets +++ b/Externals/WIL/packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets @@ -5,4 +5,7 @@ $(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories) + + + diff --git a/Externals/WIL/scripts/azure-pipelines.yml b/Externals/WIL/scripts/azure-pipelines.yml index 4b428d8748..8e0a4f7391 100644 --- a/Externals/WIL/scripts/azure-pipelines.yml +++ b/Externals/WIL/scripts/azure-pipelines.yml @@ -8,7 +8,7 @@ jobs: timeoutInMinutes: 360 pool: - vmImage: 'windows-2019' + vmImage: 'windows-2022' steps: - script: | @@ -18,7 +18,7 @@ jobs: displayName: 'Install Clang' - script: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat" + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat" if %ERRORLEVEL% NEQ 0 goto :eof call scripts\init_all.cmd --fast @@ -28,7 +28,7 @@ jobs: displayName: 'Build x86' - script: | - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" if %ERRORLEVEL% NEQ 0 goto :eof call scripts\init_all.cmd --fast @@ -37,5 +37,9 @@ jobs: call scripts\build_all.cmd displayName: 'Build x64' - - script: call scripts\runtests.cmd + # NOTE: We run the tests in the 32-bit cross-tools window out of convenience as this adds all necessary directories to + # the PATH that are necessary for finding the ASan/UBSan DLLs + - script: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"" + call scripts\runtests.cmd ~[LocalOnly] displayName: 'Run Tests' diff --git a/Externals/WIL/scripts/build_all.cmd b/Externals/WIL/scripts/build_all.cmd index 32d88f4c99..3b9489623b 100644 --- a/Externals/WIL/scripts/build_all.cmd +++ b/Externals/WIL/scripts/build_all.cmd @@ -1,4 +1,5 @@ @echo off +setlocal setlocal EnableDelayedExpansion set BUILD_ROOT=%~dp0\..\build @@ -15,23 +16,15 @@ if "%Platform%"=="x64" ( exit /B 1 ) -call :build clang debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build clang release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build clang relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build clang minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) +set COMPILERS=clang msvc +set BUILD_TYPES=debug release relwithdebinfo minsizerel -call :build msvc debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build msvc release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build msvc relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :build msvc minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) +for %%c in (%COMPILERS%) do ( + for %%b in (%BUILD_TYPES%) do ( + call :build %%c %%b + if !ERRORLEVEL! NEQ 0 ( goto :eof ) + ) +) echo All build completed successfully! @@ -47,5 +40,6 @@ if not exist %BUILD_DIR% ( pushd %BUILD_DIR% echo Building from %CD% ninja +set EXIT_CODE=%ERRORLEVEL% popd -goto :eof +exit /B %EXIT_CODE% diff --git a/Externals/WIL/scripts/init.cmd b/Externals/WIL/scripts/init.cmd index cc8efbfbe7..fc2ba016c9 100644 --- a/Externals/WIL/scripts/init.cmd +++ b/Externals/WIL/scripts/init.cmd @@ -10,8 +10,8 @@ goto :init :usage echo USAGE: echo init.cmd [--help] [-c^|--compiler ^] [-g^|--generator ^] - echo [-b^|--build-type ^] [-l^|--linker link^|lld-link] - echo [--fast] [-v^|--version X.Y.Z] + echo [-b^|--build-type ^] [-v^|--version X.Y.Z] + echo [--cppwinrt ^] [--fast] echo. echo ARGUMENTS echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc' @@ -20,6 +20,7 @@ goto :init echo 'relwithdebinfo', or 'minsizerel' echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI echo build and is typically not necessary when building locally + echo --cppwinrt Manually specifies the version of C++/WinRT to use for generating headers echo --fast Used to (slightly) reduce compile times and build output size. This is primarily echo used by the CI build machines where resources are more constrained. This switch is echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed @@ -30,10 +31,10 @@ goto :init set COMPILER= set GENERATOR= set BUILD_TYPE= - set LINKER= set CMAKE_ARGS= set BITNESS= set VERSION= + set CPPWINRT_VERSION= set FAST_BUILD=0 :parse @@ -88,29 +89,25 @@ goto :init goto :parse ) - set LINKER_SET=0 - if /I "%~1"=="-l" set LINKER_SET=1 - if /I "%~1"=="--linker" set LINKER_SET=1 - if %LINKER_SET%==1 ( - if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1 + set VERSION_SET=0 + if /I "%~1"=="-v" set VERSION_SET=1 + if /I "%~1"=="--version" set VERSION_SET=1 + if %VERSION_SET%==1 ( + if "%VERSION%" NEQ "" echo ERROR: Version already specified & call :usage & exit /B 1 + if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1 - if /I "%~2"=="link" set LINKER=link - if /I "%~2"=="lld-link" set LINKER=lld-link - if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1 + set VERSION=%~2 shift shift goto :parse ) - set VERSION_SET=0 - if /I "%~1"=="-v" set VERSION_SET=1 - if /I "%~1"=="--version" set VERSION_SET=1 - if %VERSION_SET%==1 ( - if "%VERSION%" NEQ "" echo ERROR: Version alread specified & call :usage & exit /B 1 - if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1 + if /I "%~1"=="--cppwinrt" ( + if "%CPPWINRT_VERSION%" NEQ "" echo ERROR: C++/WinRT version already specified & call :usage & exit /B 1 + if /I "%~2"=="" echo ERROR: C++/WinRT version string missing & call :usage & exit /B 1 - set VERSION=%~2 + set CPPWINRT_VERSION=%~2 shift shift @@ -132,9 +129,6 @@ goto :init :: Check for conflicting arguments if "%GENERATOR%"=="msbuild" ( if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1 - - :: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator - if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1 ) :: Select defaults @@ -145,8 +139,6 @@ goto :init if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug - if "%LINKER%"=="" set LINKER=link - :: Formulate CMake arguments if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja @@ -167,14 +159,14 @@ goto :init set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0 ) - if %LINKER%==lld-link ( - set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link - ) - if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION% + if "%CPPWINRT_VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DCPPWINRT_VERSION=%CPPWINRT_VERSION% + if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON + set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + :: Figure out the platform if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1 if "%Platform%"=="x86" ( @@ -191,7 +183,6 @@ goto :init :: Run CMake pushd %BUILD_DIR% echo Using compiler....... %COMPILER% - echo Using linker......... %LINKER% echo Using architecture... %Platform% echo Using build type..... %BUILD_TYPE% echo Using build root..... %CD% diff --git a/Externals/WIL/scripts/init_all.cmd b/Externals/WIL/scripts/init_all.cmd index 9fc1417556..a179755154 100644 --- a/Externals/WIL/scripts/init_all.cmd +++ b/Externals/WIL/scripts/init_all.cmd @@ -1,13 +1,14 @@ @echo off +setlocal +setlocal EnableDelayedExpansion :: NOTE: Architecture is picked up from the command window, so we can't control that here :( +set COMPILERS=clang msvc +set BUILD_TYPES=debug relwithdebinfo -call %~dp0\init.cmd -c clang -g ninja -b debug %* -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %* -if %ERRORLEVEL% NEQ 0 ( goto :eof ) - -call %~dp0\init.cmd -c msvc -g ninja -b debug %* -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %* -if %ERRORLEVEL% NEQ 0 ( goto :eof ) +for %%c in (%COMPILERS%) do ( + for %%b in (%BUILD_TYPES%) do ( + call %~dp0\init.cmd -c %%c -g ninja -b %%b %* + if !ERRORLEVEL! NEQ 0 ( goto :eof ) + ) +) diff --git a/Externals/WIL/scripts/runtests.cmd b/Externals/WIL/scripts/runtests.cmd index e9282fbbce..c0d3002f11 100644 --- a/Externals/WIL/scripts/runtests.cmd +++ b/Externals/WIL/scripts/runtests.cmd @@ -1,44 +1,24 @@ @echo off +setlocal setlocal EnableDelayedExpansion +set TEST_ARGS=%* + set BUILD_ROOT=%~dp0\..\build :: Unlike building, we don't need to limit ourselves to the Platform of the command window -call :execute_tests clang64debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang64release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang64relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang64minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) +set COMPILERS=clang msvc +set ARCHITECTURES=32 64 +set BUILD_TYPES=debug release relwithdebinfo minsizerel -call :execute_tests clang32debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang32release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang32relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests clang32minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) - -call :execute_tests msvc64debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc64release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc64relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc64minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) - -call :execute_tests msvc32debug -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc32release -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc32relwithdebinfo -if %ERRORLEVEL% NEQ 0 ( goto :eof ) -call :execute_tests msvc32minsizerel -if %ERRORLEVEL% NEQ 0 ( goto :eof ) +for %%c in (%COMPILERS%) do ( + for %%a in (%ARCHITECTURES%) do ( + for %%b in (%BUILD_TYPES%) do ( + call :execute_tests %%c%%a%%b + if !ERRORLEVEL! NEQ 0 ( goto :eof ) + ) + ) +) goto :eof @@ -49,19 +29,27 @@ if not exist %BUILD_DIR% ( goto :eof ) pushd %BUILD_DIR% echo Running tests from %CD% call :execute_test app witest.app.exe -if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) call :execute_test cpplatest witest.cpplatest.exe -if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) call :execute_test noexcept witest.noexcept.exe -if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) call :execute_test normal witest.exe -if %ERRORLEVEL% NEQ 0 ( popd && goto :eof ) -popd +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) +call :execute_test sanitize-address witest.asan.exe +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) +call :execute_test sanitize-undefined-behavior witest.ubsan.exe +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) +call :execute_test win7 witest.win7.exe +if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done ) -goto :eof +:execute_tests_done +set EXIT_CODE=%ERRORLEVEL% +popd +exit /B %EXIT_CODE% :execute_test if not exist tests\%1\%2 ( goto :eof ) echo Running %1 tests... -tests\%1\%2 +tests\%1\%2 %TEST_ARGS% goto :eof diff --git a/Externals/WIL/tests/CMakeLists.txt b/Externals/WIL/tests/CMakeLists.txt index a9c0fae159..897379dbef 100644 --- a/Externals/WIL/tests/CMakeLists.txt +++ b/Externals/WIL/tests/CMakeLists.txt @@ -1,19 +1,98 @@ -include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake) +include(${PROJECT_SOURCE_DIR}/cmake/common_build_flags.cmake) # All projects need to reference the WIL headers -include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${PROJECT_SOURCE_DIR}/include) # TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that include_directories(BEFORE SYSTEM ./workarounds/wrl) +# Because we don't always use msbuild, we need to run nuget manually +find_program(NUGET nuget) +if (NOT NUGET) + message(FATAL_ERROR "Unable to find the nuget CLI tool. Please install it from https://www.nuget.org/downloads and ensure it has been added to the PATH") +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") +endif() + +set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + # The build pipelines have limitations that local development environments do not, so turn a few knobs if (${FAST_BUILD}) replace_cxx_flag("/GR" "/GR-") # Disables RTTI add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD) endif() +# For some unknown reason, 'RelWithDebInfo' compiles with '/Ob1' as opposed to '/Ob2' which prevents inlining of +# functions not marked 'inline'. The reason we prefer 'RelWithDebInfo' over 'Release' is to get debug info, so manually +# revert to the desired (and default) inlining behavior as that exercises more interesting code paths +if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + # TODO: This is currently blocked by an apparent Clang bug: https://github.com/llvm/llvm-project/issues/59690 + # replace_cxx_flag("/Ob1" "/Ob2") +endif() + +set(COMMON_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CommonTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ComTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/FileSystemTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/NTResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ResourceTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ResultTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Rpc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SafeCastTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WistdTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/wiTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../natvis/wil.natvis + ) + add_subdirectory(app) add_subdirectory(cpplatest) add_subdirectory(noexcept) add_subdirectory(normal) +add_subdirectory(win7) + +set(DEBUG_BUILD FALSE) +set(HAS_DEBUG_INFO FALSE) + +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(DEBUG_BUILD TRUE) + set(HAS_DEBUG_INFO TRUE) +elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + set(HAS_DEBUG_INFO TRUE) +endif() + +set(ASAN_AVAILABLE FALSE) +set(UBSAN_AVAILABLE FALSE) +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # Address Sanitizer is available for all architectures and build types, but warns/errors if debug info is not enabled + set(ASAN_AVAILABLE ${HAS_DEBUG_INFO}) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Address Sanitizer is not available with debug libraries + set(ASAN_AVAILABLE NOT ${DEBUG_BUILD}) + set(UBSAN_AVAILABLE NOT ${DEBUG_BUILD}) +endif() + +if (${ASAN_AVAILABLE}) + add_subdirectory(sanitize-address) +endif() + +if (${UBSAN_AVAILABLE}) + add_subdirectory(sanitize-undefined-behavior) +endif() diff --git a/Externals/WIL/tests/ComApartmentVariableTests.cpp b/Externals/WIL/tests/ComApartmentVariableTests.cpp new file mode 100644 index 0000000000..ddc6148c07 --- /dev/null +++ b/Externals/WIL/tests/ComApartmentVariableTests.cpp @@ -0,0 +1,401 @@ +#include +#include +#include + +#include "common.h" + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +template +inline void LogOutput(_Printf_format_string_ PCWSTR format, args_t&&... args) +{ + OutputDebugStringW(wil::str_printf_failfast(format, wistd::forward(args)...).get()); +} + +inline bool IsComInitialized() +{ + APTTYPE type{}; APTTYPEQUALIFIER qualifier{}; + return CoGetApartmentType(&type, &qualifier) == S_OK; +} + +inline void WaitForAllComApartmentsToRundown() +{ + while (IsComInitialized()) + { + Sleep(0); + } +} + +void co_wait(const wil::unique_event& e) +{ + HANDLE raw[] = { e.get() }; + ULONG index{}; + REQUIRE_SUCCEEDED(CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, INFINITE, static_cast(std::size(raw)), raw, &index)); +} + +void RunApartmentVariableTest(void(*test)()) +{ + test(); + // Apartment variable rundown is async, wait for the last COM apartment + // to rundown before proceeding to the next test. + WaitForAllComApartmentsToRundown(); +} + +struct mock_platform +{ + static unsigned long long GetApartmentId() + { + APTTYPE type; APTTYPEQUALIFIER qualifer; + REQUIRE_SUCCEEDED(CoGetApartmentType(&type, &qualifer)); // ensure COM is inited + + // Approximate apartment Id + if (type == APTTYPE_STA) + { + REQUIRE_FALSE(GetCurrentThreadId() < APTTYPE_MAINSTA); + return GetCurrentThreadId(); + } + else + { + // APTTYPE_MTA (1), APTTYPE_NA (2), APTTYPE_MAINSTA (3) + return type; + } + } + + static auto RegisterForApartmentShutdown(IApartmentShutdown* observer) + { + const auto id = GetApartmentId(); + auto apt_observers = m_observers.find(id); + if (apt_observers == m_observers.end()) + { + m_observers.insert({ id, { observer} }); + } + else + { + apt_observers->second.emplace_back(observer); + } + return shutdown_type{ reinterpret_cast(id) }; + } + + static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie) + { + auto id = reinterpret_cast(cookie); + m_observers.erase(id); + } + + using shutdown_type = wil::unique_any; + + // This is needed to simulate the platform for unit testing. + static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + return wil::scope_exit([aptId = GetCurrentThreadId(), init = wil::CoInitializeEx(coinitFlags)]() + { + const auto id = GetApartmentId(); + auto apt_observers = m_observers.find(id); + if (apt_observers != m_observers.end()) + { + const auto& observers = apt_observers->second; + for (auto& observer : observers) + { + observer->OnUninitialize(id); + } + m_observers.erase(apt_observers); + } + }); + } + + // Enable the test hook to force losing the race + inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = 1; // enable test hook + inline static std::unordered_map>> m_observers; +}; + +auto fn() { return 42; }; +auto fn2() { return 43; }; + +wil::apartment_variable g_v1; +wil::apartment_variable g_v2; + +template +void TestApartmentVariableAllMethods() +{ + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + std::ignore = g_v1.get_or_create(fn); + + wil::apartment_variable v1; + + REQUIRE(v1.get_if() == nullptr); + REQUIRE(v1.get_or_create(fn) == 42); + int value = 43; + v1.set(value); + REQUIRE(v1.get_or_create(fn) == 43); + REQUIRE(v1.get_existing() == 43); + v1.clear(); + REQUIRE(v1.get_if() == nullptr); +} + +template +void TestApartmentVariableGetOrCreateForms() +{ + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + wil::apartment_variable v1; + REQUIRE(v1.get_or_create(fn) == 42); + v1.clear(); + REQUIRE(v1.get_or_create([&] + { + return 1; + }) == 1); + v1.clear(); + REQUIRE(v1.get_or_create() == 0); +} + +template +void TestApartmentVariableLifetimes() +{ + wil::apartment_variable av1, av2; + + { + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + auto v1 = av1.get_or_create(fn); + REQUIRE(av1.storage().size() == 1); + auto v2 = av1.get_existing(); + REQUIRE(av1.current_apartment_variable_count() == 1); + REQUIRE(v1 == v2); + } + + { + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + auto v1 = av1.get_or_create(fn); + auto v2 = av2.get_or_create(fn2); + REQUIRE((av1.current_apartment_variable_count() == 2)); + REQUIRE(v1 != v2); + REQUIRE(av1.storage().size() == 1); + } + + REQUIRE(av1.storage().size() == 0); + + { + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + auto v = av1.get_or_create(fn); + REQUIRE(av1.current_apartment_variable_count() == 1); + + std::thread([&]() // join below makes this ok + { + SetThreadDescription(GetCurrentThread(), L"STA"); + auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED); + std::ignore = av1.get_or_create(fn); + REQUIRE(av1.storage().size() == 2); + REQUIRE(av1.current_apartment_variable_count() == 1); + }).join(); + REQUIRE(av1.storage().size() == 1); + + av1.get_or_create(fn)++; + v = av1.get_existing(); + REQUIRE(v == 43); + } + + { + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + std::ignore = av1.get_or_create(fn); + REQUIRE(av1.current_apartment_variable_count() == 1); + int i = 1; + av1.set(i); + av1.clear(); + REQUIRE(av1.current_apartment_variable_count() == 0); + + // will fail fast since clear() was called. + // av1.set(1); + av1.clear_all_apartments_async().get(); + } + + REQUIRE(av1.storage().size() == 0); +} + +template +void TestMultipleApartments() +{ + wil::apartment_variable av1, av2; + + wil::unique_event t1Created{ wil::EventOptions::None }, t2Created{ wil::EventOptions::None }; + wil::unique_event t1Shutdown{ wil::EventOptions::None }, t2Shutdown{ wil::EventOptions::None }; + + auto apt1_thread = std::thread([&]() // join below makes this ok + { + SetThreadDescription(GetCurrentThread(), L"STA 1"); + auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED); + std::ignore = av1.get_or_create(fn); + std::ignore = av2.get_or_create(fn); + t1Created.SetEvent(); + co_wait(t1Shutdown); + }); + + auto apt2_thread = std::thread([&]() // join below makes this ok + { + SetThreadDescription(GetCurrentThread(), L"STA 2"); + auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED); + std::ignore = av1.get_or_create(fn); + std::ignore = av2.get_or_create(fn); + t2Created.SetEvent(); + co_wait(t2Shutdown); + }); + + t1Created.wait(); + t2Created.wait(); + av1.clear_all_apartments_async().get(); + av2.clear_all_apartments_async().get(); + + t1Shutdown.SetEvent(); + t2Shutdown.SetEvent(); + + apt1_thread.join(); + apt2_thread.join(); + + REQUIRE((wil::apartment_variable::storage().size() == 0)); +} + +template +void TestWinningApartmentAlreadyRundownRace() +{ + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + wil::apartment_variable av; + + std::ignore = av.get_or_create(fn); + const auto& storage = av.storage(); // for viewing the storage in the debugger + + wil::unique_event otherAptVarCreated{ wil::EventOptions::None }; + wil::unique_event startApartmentRundown{ wil::EventOptions::None }; + wil::unique_event comRundownComplete{ wil::EventOptions::None }; + + auto apt_thread = std::thread([&]() // join below makes this ok + { + SetThreadDescription(GetCurrentThread(), L"STA"); + auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED); + std::ignore = av.get_or_create(fn); + otherAptVarCreated.SetEvent(); + co_wait(startApartmentRundown); + }); + + otherAptVarCreated.wait(); + // we now have av in this apartment and in the STA + REQUIRE(storage.size() == 2); + // wait for async clean to complete + av.clear_all_apartments_async().get(); + startApartmentRundown.SetEvent(); + + REQUIRE(av.storage().size() == 0); + apt_thread.join(); +} + +template +void TestLosingApartmentAlreadyRundownRace() +{ + auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED); + + wil::apartment_variable av; + + std::ignore = av.get_or_create(fn); + const auto& storage = av.storage(); // for viewing the storage in the debugger + + wil::unique_event otherAptVarCreated{ wil::EventOptions::None }; + wil::unique_event startApartmentRundown{ wil::EventOptions::None }; + wil::unique_event comRundownComplete{ wil::EventOptions::None }; + + auto apt_thread = std::thread([&]() // join below makes this ok + { + SetThreadDescription(GetCurrentThread(), L"STA"); + auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED); + std::ignore = av.get_or_create(fn); + otherAptVarCreated.SetEvent(); + co_wait(startApartmentRundown); + coUninit.reset(); + comRundownComplete.SetEvent(); + }); + + otherAptVarCreated.wait(); + // we now have av in this apartment and in the STA + REQUIRE(storage.size() == 2); + auto clearAllOperation = av.clear_all_apartments_async(); + startApartmentRundown.SetEvent(); + comRundownComplete.wait(); + clearAllOperation.get(); // wait for the async rundowns to complete + + REQUIRE(av.storage().size() == 0); + apt_thread.join(); +} + +TEST_CASE("ComApartmentVariable::ShutdownRegistration", "[LocalOnly][com][unique_apartment_shutdown_registration]") +{ + { + wil::unique_apartment_shutdown_registration r; + } + + { + auto coUninit = wil::CoInitializeEx(COINIT_MULTITHREADED); + + struct ApartmentObserver : public winrt::implements + { + void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override + { + LogOutput(L"OnUninitialize %ull\n", apartmentId); + } + }; + + wil::unique_apartment_shutdown_registration apt_shutdown_registration; + unsigned long long id{}; + REQUIRE_SUCCEEDED(::RoRegisterForApartmentShutdown(winrt::make().get(), &id, apt_shutdown_registration.put())); + LogOutput(L"RoRegisterForApartmentShutdown %p\r\n", apt_shutdown_registration.get()); + // don't unregister and let the pending COM apartment rundown invoke the callback. + apt_shutdown_registration.release(); + } +} + +TEST_CASE("ComApartmentVariable::CallAllMethods", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestApartmentVariableAllMethods); +} + +TEST_CASE("ComApartmentVariable::GetOrCreateForms", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms); +} + +TEST_CASE("ComApartmentVariable::VariableLifetimes", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestApartmentVariableLifetimes); +} + +TEST_CASE("ComApartmentVariable::WinningApartmentAlreadyRundownRace", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace); +} + +TEST_CASE("ComApartmentVariable::LosingApartmentAlreadyRundownRace", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace); +} + +TEST_CASE("ComApartmentVariable::MultipleApartments", "[com][apartment_variable]") +{ + RunApartmentVariableTest(TestMultipleApartments); +} + +TEST_CASE("ComApartmentVariable::UseRealPlatformRunAllTests", "[com][apartment_variable]") +{ + if (!wil::are_apartment_variables_supported()) + { + return; + } + + RunApartmentVariableTest(TestApartmentVariableAllMethods); + RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms); + RunApartmentVariableTest(TestApartmentVariableLifetimes); + RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace); + RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace); + RunApartmentVariableTest(TestMultipleApartments); +} + +#endif diff --git a/Externals/WIL/tests/ComTests.cpp b/Externals/WIL/tests/ComTests.cpp index 009d9d7007..bccb92be26 100644 --- a/Externals/WIL/tests/ComTests.cpp +++ b/Externals/WIL/tests/ComTests.cpp @@ -6,6 +6,8 @@ #include "common.h" +#include + using namespace Microsoft::WRL; // avoid including #include , it fails to compile in noprivateapis @@ -130,6 +132,25 @@ TEST_CASE("ComTests::Test_Constructors", "[com][com_ptr]") REQUIRE(ptrMove2.get() == &helper4); REQUIRE(ptr2.get() == nullptr); } + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201907L) + SECTION("CTAD pointer construction") + { + wil::com_ptr_nothrow ptr(&helper); // explicit + REQUIRE(IUnknownFake::GetAddRef() == 1); + REQUIRE(ptr.get() == &helper); + } +#endif +} + +TEST_CASE("ComTests::Test_Make", "[com][com_ptr]") +{ + IUnknownFake::Clear(); + IUnknownFake helper; + + auto ptr = wil::make_com_ptr_nothrow(&helper); // CTAD workaround for pre-C++20 + REQUIRE(IUnknownFake::GetAddRef() == 1); + REQUIRE(ptr.get() == &helper); } TEST_CASE("ComTests::Test_Assign", "[com][com_ptr]") @@ -2231,6 +2252,90 @@ TEST_CASE("ComTests::VerifyCoGetClassObject", "[com][CoGetClassObject]") } #endif +#if defined(__IBackgroundCopyManager_INTERFACE_DEFINED__) && (__WI_LIBCPP_STD_VER >= 17) +TEST_CASE("ComTests::VerifyCoCreateEx", "[com][CoCreateInstance]") +{ + auto init = wil::CoInitializeEx_failfast(); + + { +#ifdef WIL_ENABLE_EXCEPTIONS + auto [sp1, ps1] = wil::CoCreateInstanceEx(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE((sp1 && ps1)); +#endif + auto [hr, unk] = wil::CoCreateInstanceExNoThrow(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE_SUCCEEDED(hr); + auto sp = std::get<0>(unk); + auto ps = std::get<1>(unk); + REQUIRE((sp && ps)); + auto [sp3, ps3] = wil::CoCreateInstanceExFailFast(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE((sp3 && ps3)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + { + auto [ps, pf] = wil::CoCreateInstanceEx(__uuidof(ShellLink), CLSCTX_INPROC_SERVER); + std::ignore = ps->IsDirty(); + std::ignore = pf->IsDirty(); + } +#endif +} + +TEST_CASE("ComTests::VerifyCoCreateInstanceExNoThrowMissingInterface", "[com][CoCreateInstance]") +{ + auto init = wil::CoInitializeEx_failfast(); + + { + // IPropertyBag is not implemented + auto [error, result] = wil::CoCreateInstanceExNoThrow + (__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE(error == E_NOINTERFACE); + REQUIRE(std::get<0>(result).get() == nullptr); + REQUIRE(std::get<1>(result).get() == nullptr); + REQUIRE(std::get<2>(result).get() == nullptr); + } +} + +TEST_CASE("ComTests::VerifyTryCoCreateInstanceMissingInterface", "[com][CoCreateInstance]") +{ + auto init = wil::CoInitializeEx_failfast(); + + // request some implemented, one not (IPropertyBag), partial results enabled + { + auto [sp, pb] = wil::TryCoCreateInstanceEx + (__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE(sp != nullptr); + REQUIRE(pb == nullptr); + } + { + auto [sp, pb] = wil::TryCoCreateInstanceExNoThrow + (__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE(sp != nullptr); + REQUIRE(pb == nullptr); + } + { + auto [sp, pb] = wil::TryCoCreateInstanceExFailFast + (__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER); + REQUIRE(sp != nullptr); + REQUIRE(pb == nullptr); + } +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("ComTests::VerifyQueryMultipleInterfaces", "[com][com_multi_query]") +{ + auto init = wil::CoInitializeEx_failfast(); + + auto mgr = wil::CoCreateInstance(CLSCTX_LOCAL_SERVER); + auto [sp, ps] = wil::com_multi_query(mgr.get()); + REQUIRE(sp); + REQUIRE(ps); + auto [sp1, pb] = wil::try_com_multi_query(mgr.get()); + REQUIRE(sp1); + REQUIRE(!pb); +} +#endif +#endif // __IBackgroundCopyManager_INTERFACE_DEFINED__ + #ifdef __IObjectWithSite_INTERFACE_DEFINED__ TEST_CASE("ComTests::VerifyComSetSiteNullIsMoveOnly", "[com][com_set_site]") { diff --git a/Externals/WIL/tests/CommonTests.cpp b/Externals/WIL/tests/CommonTests.cpp index d8eb4b4a6f..7027a79903 100644 --- a/Externals/WIL/tests/CommonTests.cpp +++ b/Externals/WIL/tests/CommonTests.cpp @@ -159,7 +159,7 @@ enum class EClassTest }; DEFINE_ENUM_FLAG_OPERATORS(EClassTest); -enum ERawTest +enum ERawTest : unsigned int { ER_None = 0x0, ER_One = 0x1, diff --git a/Externals/WIL/tests/CppWinRT20Tests.cpp b/Externals/WIL/tests/CppWinRT20Tests.cpp index 2b911ad80e..2a4ef68c8c 100644 --- a/Externals/WIL/tests/CppWinRT20Tests.cpp +++ b/Externals/WIL/tests/CppWinRT20Tests.cpp @@ -3,6 +3,8 @@ // However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler' // global function pointer should be set, so these should all run successfully +#include // Must be included before base.h + #include #include diff --git a/Externals/WIL/tests/CppWinRTTests.cpp b/Externals/WIL/tests/CppWinRTTests.cpp index 199f7fd2ba..c8ad183476 100644 --- a/Externals/WIL/tests/CppWinRTTests.cpp +++ b/Externals/WIL/tests/CppWinRTTests.cpp @@ -1,5 +1,10 @@ #include +#include +#include +#include +#include +#include // Verify can include a second time to unlock more features #include "catch.hpp" @@ -22,6 +27,145 @@ static const HRESULT cppwinrt_mapped_hresults[] = E_OUTOFMEMORY, }; +template auto copy_thing(T const& src) +{ + return std::decay_t(src); +} + +template +void CheckMapVector(std::vector> const& test, std::map const& src) +{ + REQUIRE(test.size() == src.size()); + for (auto&& i : test) + { + REQUIRE(i.Value() == src.at(i.Key())); + } +} + +struct vector_like +{ + uint32_t Size() const { return 100; } + int GetAt(uint32_t) const { return 15; } + + uint32_t GetMany(uint32_t start, winrt::array_view items) const + { + if (start > 0) + { + throw winrt::hresult_out_of_bounds(); + } + uint32_t const to_fill = (std::min)(items.size(), Size()); + std::fill_n(items.begin(), to_fill, GetAt(0)); + return to_fill; + } +}; + +struct iterator_like +{ + static const uint32_t total = 20; + mutable uint32_t remaining = total; + int Current() const { return 3; } + + uint32_t GetMany(winrt::array_view items) const + { + auto to_copy = (std::min)(items.size(), remaining); + std::fill_n(items.begin(), to_copy, Current()); + remaining -= to_copy; + return to_copy; + } +}; + + +struct iterable_like +{ + auto First() const { return iterator_like{}; } +}; + +struct unstable_vector : winrt::implements> +{ + auto Size() { return 4; } + int GetAt(uint32_t) { return 7; } + + uint32_t GetMany(uint32_t, winrt::array_view items) + { + std::fill(items.begin(), items.end(), GetAt(0)); + return items.size(); + } + + bool IndexOf(int, uint32_t) { throw winrt::hresult_not_implemented(); } +}; + +TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]") +{ + winrt::init_apartment(); + { + std::vector src_vector = { L"foo", L"bar", L"bas" }; + auto sv = winrt::single_threaded_vector(copy_thing(src_vector)); + REQUIRE(wil::to_vector(sv) == src_vector); + REQUIRE(wil::to_vector(sv.GetView()) == src_vector); + REQUIRE(wil::to_vector(sv.First()) == src_vector); + REQUIRE(wil::to_vector(sv.First()) == src_vector); + REQUIRE(wil::to_vector(sv.as>()) == src_vector); + } + { + std::vector src_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + auto sv = winrt::single_threaded_vector(copy_thing(src_vector)); + REQUIRE(wil::to_vector(sv) == src_vector); + REQUIRE(wil::to_vector(sv.GetView()) == src_vector); + REQUIRE(wil::to_vector(sv.First()) == src_vector); + REQUIRE(wil::to_vector(sv.as>()) == src_vector); + } + { + std::vector src_vector; + auto sv = winrt::single_threaded_vector(copy_thing(src_vector)); + REQUIRE(wil::to_vector(sv) == src_vector); + REQUIRE(wil::to_vector(sv.GetView()) == src_vector); + REQUIRE(wil::to_vector(sv.First()) == src_vector); + REQUIRE(wil::to_vector(sv.as>()) == src_vector); + } + { + std::map src_map{{L"kittens", L"fluffy"}, {L"puppies", L"cute"}}; + auto sm = winrt::single_threaded_map(copy_thing(src_map)); + CheckMapVector(wil::to_vector(sm), src_map); + CheckMapVector(wil::to_vector(sm.GetView()), src_map); + CheckMapVector(wil::to_vector(sm.First()), src_map); + } + { + winrt::Windows::Foundation::Collections::PropertySet props; + props.Insert(L"kitten", winrt::box_value(L"fluffy")); + props.Insert(L"puppy", winrt::box_value(25)); + auto converted = wil::to_vector(props); + REQUIRE(converted.size() == props.Size()); + for (auto&& kv : converted) + { + if (kv.Key() == L"kitten") + { + REQUIRE(kv.Value().as() == L"fluffy"); + } + else if (kv.Key() == L"puppy") + { + REQUIRE(kv.Value().as() == 25); + } + else + { + REQUIRE(false); + } + } + } + + REQUIRE_THROWS(wil::to_vector(winrt::make())); + + auto ilike = wil::to_vector(iterable_like{}); + REQUIRE(ilike.size() == iterator_like::total); + for (auto&& i : ilike) REQUIRE(i == iterator_like{}.Current()); + + auto vlike = wil::to_vector(vector_like{}); + REQUIRE(vlike.size() == vector_like{}.Size()); + for (auto&& i : vlike) REQUIRE(i == vector_like{}.GetAt(0)); + + winrt::clear_factory_cache(); + winrt::uninit_apartment(); +} + TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]") { auto test = [](HRESULT hr) @@ -142,3 +286,159 @@ TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]") // NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior // that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION) } + +TEST_CASE("CppWinRTTests::ModuleReference", "[cppwinrt]") +{ + auto peek_module_ref_count = []() + { + ++winrt::get_module_lock(); + return --winrt::get_module_lock(); + }; + + auto initial = peek_module_ref_count(); + + // Basic test: Construct and destruct. + { + auto module_ref = wil::winrt_module_reference(); + REQUIRE(peek_module_ref_count() == initial + 1); + } + REQUIRE(peek_module_ref_count() == initial); + + // Fancy test: Copy object with embedded reference. + { + struct object_with_ref + { + wil::winrt_module_reference ref; + }; + object_with_ref o1; + REQUIRE(peek_module_ref_count() == initial + 1); + auto o2 = o1; + REQUIRE(peek_module_ref_count() == initial + 2); + o1 = o2; + REQUIRE(peek_module_ref_count() == initial + 2); + o2 = std::move(o1); + REQUIRE(peek_module_ref_count() == initial + 2); + } + REQUIRE(peek_module_ref_count() == initial); +} + +#if (!defined(__clang__) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED) + +// Define our own custom dispatcher that we can force it to behave in certain ways. +// wil::resume_foreground supports any dispatcher that has a dispatcher_traits. + +namespace test +{ + enum class TestDispatcherPriority + { + Normal = 0, + Weird = 1, + }; + + using TestDispatcherHandler = winrt::delegate<>; + + enum class TestDispatcherMode + { + Dispatch, + RaceDispatch, + Orphan, + Fail, + }; + + struct TestDispatcher + { + TestDispatcher() = default; + TestDispatcher(TestDispatcher const&) = delete; + + TestDispatcherMode mode = TestDispatcherMode::Dispatch; + TestDispatcherPriority expected_priority = TestDispatcherPriority::Normal; + + void TryEnqueue(TestDispatcherPriority priority, TestDispatcherHandler const& handler) const + { + REQUIRE(priority == expected_priority); + + if (mode == TestDispatcherMode::Fail) + { + throw winrt::hresult_not_implemented(); + } + + if (mode == TestDispatcherMode::RaceDispatch) + { + handler(); + return; + } + + std::ignore = [](auto mode, auto handler) ->winrt::fire_and_forget + { + co_await winrt::resume_background(); + if (mode == TestDispatcherMode::Dispatch) + { + handler(); + } + }(mode, handler); + } + }; +} + +namespace wil::details +{ + template<> + struct dispatcher_traits + { + using Priority = test::TestDispatcherPriority; + using Handler = test::TestDispatcherHandler; + using Scheduler = dispatcher_TryEnqueue; + }; +} + +TEST_CASE("CppWinRTTests::ResumeForegroundTests", "[cppwinrt]") +{ + // Verify that the DispatcherQueue version has been unlocked. + using Verify = decltype(wil::resume_foreground(winrt::Windows::System::DispatcherQueue{ nullptr })); + static_assert(wistd::is_trivial_v || !wistd::is_trivial_v); + + []() -> winrt::Windows::Foundation::IAsyncAction + { + test::TestDispatcher dispatcher; + + // Normal case: Resumes on new thread. + dispatcher.mode = test::TestDispatcherMode::Dispatch; + co_await wil::resume_foreground(dispatcher); + + // Race case: Resumes before TryEnqueue returns. + dispatcher.mode = test::TestDispatcherMode::RaceDispatch; + co_await wil::resume_foreground(dispatcher); + + // Orphan case: Never resumes, detected when handler is destructed without ever being invoked. + dispatcher.mode = test::TestDispatcherMode::Orphan; + bool seen = false; + try + { + co_await wil::resume_foreground(dispatcher); + } + catch (winrt::hresult_error const& e) + { + seen = e.code() == HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)); + } + REQUIRE(seen); + + // Fail case: Can't even schedule the resumption. + dispatcher.mode = test::TestDispatcherMode::Fail; + seen = false; + try + { + co_await wil::resume_foreground(dispatcher); + } + catch (winrt::hresult_not_implemented const&) + { + seen = true; + } + REQUIRE(seen); + + // Custom priority. + dispatcher.mode = test::TestDispatcherMode::Dispatch; + dispatcher.expected_priority = test::TestDispatcherPriority::Weird; + co_await wil::resume_foreground(dispatcher, test::TestDispatcherPriority::Weird); + }().get(); +} +#endif // coroutines diff --git a/Externals/WIL/tests/FakeWinRTTypes.h b/Externals/WIL/tests/FakeWinRTTypes.h index 70e607a233..d4aed5943c 100644 --- a/Externals/WIL/tests/FakeWinRTTypes.h +++ b/Externals/WIL/tests/FakeWinRTTypes.h @@ -42,7 +42,7 @@ struct WinRTStorage { Microsoft::WRL::Wrappers::HString value; - HRESULT CopyTo(HSTRING* result) + HRESULT CopyTo(HSTRING* result) const { return value.CopyTo(result); } @@ -63,7 +63,7 @@ struct WinRTStorage value = {}; } - bool Equals(HSTRING val) + bool Equals(HSTRING val) const { return value == val; } @@ -207,7 +207,7 @@ private: WinRTStorage m_storage; }; -template +template struct FakeVector : Microsoft::WRL::RuntimeClass< ABI::Windows::Foundation::Collections::IVector, ABI::Windows::Foundation::Collections::IVectorView> @@ -292,7 +292,7 @@ struct FakeVector : Microsoft::WRL::RuntimeClass< for (size_t i = index + 1; i < m_size; ++i) { - wistd::swap_wil(m_data[i - 1], m_data[i]); + wistd::swap_wil(m_data[i], m_data[i - 1]); } m_data[--m_size].Reset(); @@ -349,8 +349,8 @@ struct FakeVector : Microsoft::WRL::RuntimeClass< count = (count > capacity) ? capacity : count; HRESULT hr = S_OK; - unsigned i = 0; - for (; (i < count) && SUCCEEDED(hr); ++i) + unsigned i; + for (i = 0; (i < count) && SUCCEEDED(hr); ++i) { hr = m_data[startIndex + i].CopyTo(value + i); } diff --git a/Externals/WIL/tests/FileSystemTests.cpp b/Externals/WIL/tests/FileSystemTests.cpp index 96aae140c4..a6e8b5caa4 100644 --- a/Externals/WIL/tests/FileSystemTests.cpp +++ b/Externals/WIL/tests/FileSystemTests.cpp @@ -34,6 +34,13 @@ bool DirectoryExists(_In_ PCWSTR path) (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } +bool FileExists(_In_ PCWSTR path) +{ + DWORD dwAttrib = GetFileAttributesW(path); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES); +} + TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]") { wchar_t basePath[MAX_PATH]; @@ -80,6 +87,99 @@ TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]") REQUIRE_FALSE(DirectoryExists(absoluteTestPath4)); } +TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveDoesNotTraverseWithoutAHandle", "[filesystem]") +{ + auto CreateRelativePath = [](PCWSTR root, PCWSTR name) + { + wil::unique_hlocal_string path; + REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path)); + return path; + }; + + wil::unique_cotaskmem_string tempPath; + REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath)); + const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests"); + REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get())); + + auto scopeGuard = wil::scope_exit([&] + { + wil::RemoveDirectoryRecursiveNoThrow(basePath.get()); + }); + + // Try to delete a directory whose handle is already taken. + const auto folderToRecurse = CreateRelativePath(basePath.get(), L"folderToRecurse"); + REQUIRE(::CreateDirectoryW(folderToRecurse.get(), nullptr)); + + const auto subfolderWithHandle = CreateRelativePath(folderToRecurse.get(), L"subfolderWithHandle"); + REQUIRE(::CreateDirectoryW(subfolderWithHandle.get(), nullptr)); + + const auto childOfSubfolder = CreateRelativePath(subfolderWithHandle.get(), L"childOfSubfolder"); + REQUIRE(::CreateDirectoryW(childOfSubfolder.get(), nullptr)); + + // Passing a 0 in share flags only allows metadata query on this file by other processes. + // This should fail with a sharing violation error when any other action is taken. + wil::unique_hfile subFolderHandle(::CreateFileW(subfolderWithHandle.get(), GENERIC_ALL, + 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + REQUIRE(subFolderHandle); + + REQUIRE(wil::RemoveDirectoryRecursiveNoThrow(folderToRecurse.get()) == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)); + + // Release the handle to allow cleanup. + subFolderHandle.reset(); +} + +TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteReadOnlyFiles", "[filesystem]") +{ + auto CreateRelativePath = [](PCWSTR root, PCWSTR name) + { + wil::unique_hlocal_string path; + REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path)); + return path; + }; + + auto CreateReadOnlyFile = [](PCWSTR path) + { + wil::unique_hfile fileHandle(CreateFileW(path, 0, + 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_READONLY, nullptr)); + REQUIRE(fileHandle); + }; + + wil::unique_cotaskmem_string tempPath; + REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath)); + const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests"); + REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get())); + + auto scopeGuard = wil::scope_exit([&] + { + wil::RemoveDirectoryRecursiveNoThrow(basePath.get(), wil::RemoveDirectoryOptions::RemoveReadOnly); + }); + + // Create a reparse point and a target folder that shouldn't get deleted + auto folderToDelete = CreateRelativePath(basePath.get(), L"folderToDelete"); + REQUIRE(::CreateDirectoryW(folderToDelete.get(), nullptr)); + + auto topLevelReadOnly = CreateRelativePath(folderToDelete.get(), L"topLevelReadOnly.txt"); + CreateReadOnlyFile(topLevelReadOnly.get()); + + auto subLevel = CreateRelativePath(folderToDelete.get(), L"subLevel"); + REQUIRE(::CreateDirectoryW(subLevel.get(), nullptr)); + + auto subLevelReadOnly = CreateRelativePath(subLevel.get(), L"subLevelReadOnly.txt"); + CreateReadOnlyFile(subLevelReadOnly.get()); + + // Delete will fail without the RemoveReadOnlyFlag + REQUIRE_FAILED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get())); + REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get(), wil::RemoveDirectoryOptions::RemoveReadOnly)); + + // Verify all files have been deleted + REQUIRE_FALSE(FileExists(subLevelReadOnly.get())); + REQUIRE_FALSE(DirectoryExists(subLevel.get())); + + REQUIRE_FALSE(FileExists(topLevelReadOnly.get())); + REQUIRE_FALSE(DirectoryExists(folderToDelete.get())); +} + #ifdef WIL_ENABLE_EXCEPTIONS // Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/ // This test verifies the ability of RemoveDirectoryRecursive to be able to delete files @@ -440,8 +540,28 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]") REQUIRE(wcscmp(path.get(), path2.get()) == 0); REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path)); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto wstringPath = wil::GetModuleFileNameW(nullptr); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); +#endif } +#ifdef WIL_ENABLE_EXCEPTIONS +wil::unique_cotaskmem_string NativeGetModuleFileNameWrap(HANDLE processHandle, HMODULE moduleHandle) +{ + DWORD size = MAX_PATH * 4; + auto path = wil::make_cotaskmem_string_nothrow(nullptr, size); + + DWORD copied = processHandle ? + ::GetModuleFileNameExW(processHandle, moduleHandle, path.get(), size) : + ::GetModuleFileNameW(moduleHandle, path.get(), size); + REQUIRE(copied < size); + + return path; +} +#endif + TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]") { wil::unique_cotaskmem_string path; @@ -455,6 +575,58 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]") REQUIRE(wcscmp(path.get(), path2.get()) == 0); REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path)); + +#ifdef WIL_ENABLE_EXCEPTIONS + auto wstringPath = wil::GetModuleFileNameExW(nullptr, nullptr); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get()); + + wstringPath = wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), nullptr).get()); + + wstringPath = wil::GetModuleFileNameW(nullptr); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get()); + + HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll"); + + wstringPath = wil::GetModuleFileNameExW(nullptr, kernel32); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get()); + + wstringPath = wil::GetModuleFileNameExW(GetCurrentProcess(), kernel32); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), kernel32).get()); + + wstringPath = wil::GetModuleFileNameW(kernel32); + REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str())); + REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get()); +#endif } +TEST_CASE("FileSystemTests::QueryFullProcessImageNameW and GetModuleFileNameW", "[filesystem]") +{ +#ifdef WIL_ENABLE_EXCEPTIONS + auto procName = wil::QueryFullProcessImageNameW(); + auto moduleName = wil::GetModuleFileNameW(); + REQUIRE(procName == moduleName); +#endif +} + +TEST_CASE("FileSystemTests::QueryFullProcessImageNameW", "[filesystem]") +{ + WCHAR fullName[MAX_PATH * 4]; + DWORD fullNameSize = ARRAYSIZE(fullName); + REQUIRE(::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, fullName, &fullNameSize)); + + wil::unique_cotaskmem_string path; + REQUIRE_SUCCEEDED(wil::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, path)); + REQUIRE(wcscmp(fullName, path.get()) == 0); + + wil::unique_cotaskmem nativePath; + REQUIRE_SUCCEEDED((wil::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, path))); +} + + #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) diff --git a/Externals/WIL/tests/NtResultTests.cpp b/Externals/WIL/tests/NtResultTests.cpp new file mode 100644 index 0000000000..a51119eb99 --- /dev/null +++ b/Externals/WIL/tests/NtResultTests.cpp @@ -0,0 +1,165 @@ + +#include +#include + +#include "common.h" + +#define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL) +#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L) +#define STATUS_INVALID_CONNECTION ((NTSTATUS)0xC0000140L) +#define E_LOAD_NAMESERVICE_FAILED ((HRESULT)0x80000140L) + +TEST_CASE("NtResultTests::NtReturn", "[result]") +{ + auto status = []() + { + NT_RETURN_NTSTATUS(STATUS_INVALID_CONNECTION); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG"); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter"); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + NT_RETURN_IF_NTSTATUS_FAILED(STATUS_INVALID_CONNECTION); + return STATUS_SUCCESS; + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + NT_RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter"); + return STATUS_SUCCESS; + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + NT_RETURN_IF_NTSTATUS_FAILED(STATUS_SUCCESS); + return STATUS_INVALID_CONNECTION; + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("NtResultTests::NtThrowCatch", "[result]") +{ + // Throw NTSTATUS with immediate conversion to HRESULT. HRESULT would appear in the logs. + auto hr = []() + { + try + { + THROW_NTSTATUS(STATUS_INVALID_CONNECTION); + } + CATCH_RETURN(); + }(); + // THROW_NTSTATUS converts NTSTATUS to HRESULT through WIN32 error code. + REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); + + // Verify that conversion NTSTATUS -> HRESULT -> NTSTATUS is not 1:1. + auto status = []() + { + try + { + THROW_HR(wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); + } + NT_CATCH_RETURN(); + }(); + if (wil::details::g_pfnRtlNtStatusToDosErrorNoTeb) + { + REQUIRE(status != STATUS_INVALID_CONNECTION); + } + else + { + REQUIRE(status == STATUS_INVALID_CONNECTION); + } + + // Throw HRESULT with conversion to NTSTATUS on a best effort. NTSTATUS would appear in the logs. + status = []() + { + try + { + THROW_HR(__HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); + } + NT_CATCH_RETURN(); + }(); + REQUIRE(status == STATUS_OBJECT_PATH_NOT_FOUND); + + // Throw HRESULT with conversion to NTSTATUS on a best effort that maps to generic error. NTSTATUS would appear in the logs. + status = []() + { + try + { + THROW_HR(E_LOAD_NAMESERVICE_FAILED); + } + NT_CATCH_RETURN(); + }(); + REQUIRE(status == STATUS_INTERNAL_ERROR); + + // Throw NTSTATUS without conversion. NTSTATUS would appear in the logs. + status = []() + { + try + { + THROW_NTSTATUS(STATUS_INVALID_CONNECTION); + } + NT_CATCH_RETURN(); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + try + { + THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); + } + NT_CATCH_RETURN(); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + status = []() + { + try + { + THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS with custom catch"); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + + return wil::StatusFromCaughtException(); + } + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); + + hr = []() + { + try + { + THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); + } + CATCH_RETURN(); + }(); + REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION)); + + status = []() + { + try + { + THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS"); + } + NT_CATCH_RETURN_MSG("Catching STATUS_INVALID_CONNECTION thrown by NT_THROW_NTSTATUS_MSG"); + }(); + REQUIRE(status == STATUS_INVALID_CONNECTION); +} +#endif diff --git a/Externals/WIL/tests/ResourceTests.cpp b/Externals/WIL/tests/ResourceTests.cpp index c16daf1b69..72555f11cb 100644 --- a/Externals/WIL/tests/ResourceTests.cpp +++ b/Externals/WIL/tests/ResourceTests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,14 @@ TEST_CASE("ResourceTests::TestLastErrorContext", "[resource][last_error_context] SetLastError(1); } REQUIRE(GetLastError() == 1); + + // The value in the context is unimpacted by other things changing the last error + { + SetLastError(42); + auto error42 = wil::last_error_context(); + SetLastError(1); + REQUIRE(error42.value() == 42); + } } TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]") @@ -267,6 +276,40 @@ void UniqueProcessInfo() } #endif +// Compilation only test... +#ifdef WIL_ENABLE_EXCEPTIONS +void NoexceptConstructibleTest() +{ + using BaseStorage = wil::details::unique_storage; + + struct ThrowingConstructor : BaseStorage + { + ThrowingConstructor() = default; + explicit ThrowingConstructor(HANDLE) __WI_NOEXCEPT_(false) {} + }; + + struct ProtectedConstructor : BaseStorage + { + protected: + ProtectedConstructor() = default; + explicit ProtectedConstructor(HANDLE) WI_NOEXCEPT {} + }; + + // wil::unique_handle is one of the many types which are expected to be noexcept + // constructible since they don't perform any "advanced" initialization. + static_assert(wistd::is_nothrow_default_constructible_v, "wil::unique_any_t should always be nothrow default constructible"); + static_assert(wistd::is_nothrow_constructible_v, "wil::unique_any_t should be noexcept if the storage is"); + + // The inverse: A throwing storage constructor. + static_assert(wistd::is_nothrow_default_constructible_v>, "wil::unique_any_t should always be nothrow default constructible"); + static_assert(!wistd::is_nothrow_constructible_v, HANDLE>, "wil::unique_any_t shouldn't be noexcept if the storage isn't"); + + // With a protected constructor wil::unique_any_t will be unable to correctly + // "forward" the noexcept attribute, but the code should still compile. + wil::unique_any_t p{ INVALID_HANDLE_VALUE }; +} +#endif + struct FakeComInterface { void AddRef() @@ -732,4 +775,90 @@ TEST_CASE("DefaultTemplateParamCompiles", "[resource]") wil::unique_midl_ptr<> g; wil::unique_cotaskmem_ptr<> h; + wil::unique_mapview_ptr<> i; +} + +TEST_CASE("UniqueInvokeCleanupMembers", "[resource]") +{ + // Case 1 - unique_ptr<> for a T* that has a "destroy" member + struct ThingWithDestroy + { + bool destroyed = false; + void destroy() { destroyed = true; }; + }; + ThingWithDestroy toDestroy; + wil::unique_any p(&toDestroy); + p.reset(); + REQUIRE(!p); + REQUIRE(toDestroy.destroyed); + + // Case 2 - unique_struct calling a member, like above + struct ThingToDestroy2 + { + bool* destroyed; + void destroy() { *destroyed = true; }; + }; + bool structDestroyed = false; + { + wil::unique_struct other; + other.destroyed = &structDestroyed; + REQUIRE(!structDestroyed); + } + REQUIRE(structDestroyed); +} + +struct ITokenTester : IUnknown +{ + virtual void DirectClose(DWORD_PTR token) = 0; +}; + +struct TokenTester : ITokenTester +{ + IFACEMETHOD_(ULONG, AddRef)() override { return 2; } + IFACEMETHOD_(ULONG, Release)() override { return 1; } + IFACEMETHOD(QueryInterface)(REFIID, void**) { return E_NOINTERFACE; } + void DirectClose(DWORD_PTR token) override { + m_closed = (token == m_closeToken); + } + bool m_closed = false; + DWORD_PTR m_closeToken; +}; + +void MyTokenTesterCloser(ITokenTester* tt, DWORD_PTR token) +{ + tt->DirectClose(token); +} + +TEST_CASE("ComTokenCloser", "[resource]") +{ + using token_tester_t = wil::unique_com_token; + + TokenTester tt; + tt.m_closeToken = 4; + { + token_tester_t tmp{ &tt, 4 }; + } + REQUIRE(tt.m_closed); +} + +TEST_CASE("ComTokenDirectCloser", "[resource]") +{ + using token_tester_t = wil::unique_com_token; + + TokenTester tt; + tt.m_closeToken = 4; + { + token_tester_t tmp{ &tt, 4 }; + } + REQUIRE(tt.m_closed); +} + +TEST_CASE("UniqueCloseClipboardCall", "[resource]") +{ +#if defined(__WIL__WINUSER_) && !defined(NOCLIPBOARD) + if (auto clip = wil::open_clipboard(nullptr)) + { + REQUIRE(::EmptyClipboard()); + } +#endif } diff --git a/Externals/WIL/tests/ResultTests.cpp b/Externals/WIL/tests/ResultTests.cpp index be93719119..dc18448e35 100644 --- a/Externals/WIL/tests/ResultTests.cpp +++ b/Externals/WIL/tests/ResultTests.cpp @@ -1,7 +1,10 @@ #include #include + +#if (NTDDI_VERSION >= NTDDI_WIN8) #include +#endif #include @@ -323,7 +326,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]") RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }(); - REQUIRE(failures.size() == 0); + REQUIRE(failures.empty()); REQUIRE(hr == E_OUTOFMEMORY); } failures.clear(); @@ -339,7 +342,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]") { throw std::bad_alloc(); }); - REQUIRE(failures.size() == 0); + REQUIRE(failures.empty()); REQUIRE(hr == E_OUTOFMEMORY); } failures.clear(); @@ -436,16 +439,16 @@ void ExceptionHandlingCompilationTest() try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {} try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {} - HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&] + wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&] { THROW_HR(E_FAIL); }); - hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&] + wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&] { }); - hr = wil::ResultFromException([&] + wil::ResultFromException([&] { }); @@ -479,7 +482,7 @@ TEST_CASE("ResultTests::ErrorMacros", "[result]") } // The originate helper isn't compatible with CX so don't test it in that mode. -#ifndef __cplusplus_winrt +#if !defined(__cplusplus_winrt) && (NTDDI_VERSION >= NTDDI_WIN8) TEST_CASE("ResultTests::NoOriginationByDefault", "[result]") { ::wil::SetOriginateErrorCallback(nullptr); @@ -572,4 +575,17 @@ TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]") }(); REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation)); } -#endif // __cplusplus_winrt +#endif + +TEST_CASE("ResultTests::ReportDoesNotChangeLastError", "[result]") +{ + decltype(wil::details::g_pfnLoggingCallback) oopsie = [](wil::FailureInfo const&) noexcept + { + ::SetLastError(ERROR_ABANDON_HIBERFILE); + }; + auto swap = witest::AssignTemporaryValue(&wil::details::g_pfnLoggingCallback, oopsie); + + ::SetLastError(ERROR_ABIOS_ERROR); + LOG_IF_WIN32_BOOL_FALSE(FALSE); + REQUIRE(::GetLastError() == ERROR_ABIOS_ERROR); +} diff --git a/Externals/WIL/tests/Rpc.cpp b/Externals/WIL/tests/Rpc.cpp index e66a73eaef..3ffeee8980 100644 --- a/Externals/WIL/tests/Rpc.cpp +++ b/Externals/WIL/tests/Rpc.cpp @@ -11,8 +11,8 @@ void RpcMethodReturnsVoid(ULONG toRaise) } struct FOO_CONTEXT_T {}; -typedef FOO_CONTEXT_T* FOO_CONTEXT; -typedef FOO_CONTEXT* PFOO_CONTEXT; +using FOO_CONTEXT = FOO_CONTEXT_T*; +using PFOO_CONTEXT = FOO_CONTEXT*; void CloseContextHandle(_Inout_ PFOO_CONTEXT) { diff --git a/Externals/WIL/tests/StlTests.cpp b/Externals/WIL/tests/StlTests.cpp index e006202ea5..db945c5e47 100644 --- a/Externals/WIL/tests/StlTests.cpp +++ b/Externals/WIL/tests/StlTests.cpp @@ -45,3 +45,140 @@ TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]") wil::secure_vector sensitiveBytes(32, dummy{ 'a' }); } } + +#if __WI_LIBCPP_STD_VER >= 17 + +struct CustomNoncopyableString +{ + CustomNoncopyableString() = default; + CustomNoncopyableString(const CustomNoncopyableString&) = delete; + void operator=(const CustomNoncopyableString&) = delete; + + constexpr operator PCSTR() const { return "hello"; } + constexpr operator PCWSTR() const { return L"w-hello"; } +}; + +TEST_CASE("StlTests::TestZStringView", "[stl][zstring_view]") +{ + // Test empty cases + REQUIRE(wil::zstring_view{}.length() == (size_t)0u); + REQUIRE(wil::zstring_view{}.data() == nullptr); + REQUIRE(wil::zstring_view{}.c_str() == nullptr); + + // Test empty string cases + REQUIRE(wil::zstring_view{""}[0] == '\0'); + REQUIRE(wil::zstring_view{""}.c_str()[0] == '\0'); + REQUIRE(wil::zstring_view{""}.length() == 0); + + // Test different constructor equality + constexpr wil::zstring_view fromLiteral = "abc"; + REQUIRE(fromLiteral.length() == strlen("abc")); + + std::string stlString = "abc"; + wil::zstring_view fromString(stlString); + wil::zstring_view fromPtr(stlString.data()); + + static constexpr char charArray[] = "abc"; + constexpr wil::zstring_view fromArray(charArray); + + static constexpr char extendedCharArray[] = "abc\0\0\0\0\0"; + constexpr wil::zstring_view fromExtendedArray(extendedCharArray); + + wil::zstring_view copy = fromLiteral; + + REQUIRE(fromLiteral == stlString); + REQUIRE(fromLiteral == fromString); + REQUIRE(fromLiteral == fromArray); + REQUIRE(fromLiteral == fromExtendedArray); + REQUIRE(fromLiteral == copy); + + // Test decay to std::string_view + std::string_view sv = fromLiteral; + REQUIRE(sv == fromLiteral); + + // Test operator[] + REQUIRE(fromLiteral[0] == 'a'); + REQUIRE(fromLiteral[1] == 'b'); + REQUIRE(fromLiteral[2] == 'c'); + REQUIRE(fromLiteral[3] == '\0'); + + // Test constructing with no NULL in range + static constexpr char badCharArray[2][3] = {{'a', 'b', 'c' }, {'a', 'b', 'c' }}; + REQUIRE_CRASH((wil::zstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) })); + REQUIRE_CRASH((wil::zstring_view{ badCharArray[0] })); + + // Test constructing with a NULL one character past the valid range, guarding against off-by-one errors + // Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer + static constexpr char badCharArrayOffByOne[2][3] = {{'a', 'b', 'c' }, {}}; + const wil::zstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0])); + REQUIRE(fromLiteral == fromTerminatedCharArray); + REQUIRE_CRASH((wil::zstring_view{ badCharArrayOffByOne[0] })); + + // Test constructing from custom string type + CustomNoncopyableString customString; + wil::zstring_view fromCustomString(customString); + REQUIRE(fromCustomString == (PCSTR)customString); +} + +TEST_CASE("StlTests::TestZWStringView", "[stl][zstring_view]") +{ + // Test empty cases + REQUIRE(wil::zwstring_view{}.length() == (size_t)0u); + REQUIRE(wil::zwstring_view{}.data() == nullptr); + REQUIRE(wil::zwstring_view{}.c_str() == nullptr); + + // Test empty string cases + REQUIRE(wil::zwstring_view{L""}[0] == L'\0'); + REQUIRE(wil::zwstring_view{L""}.c_str()[0] == L'\0'); + REQUIRE(wil::zwstring_view{L""}.length() == 0); + + // Test different constructor equality + constexpr wil::zwstring_view fromLiteral = L"abc"; + REQUIRE(fromLiteral.length() == wcslen(L"abc")); + + std::wstring stlString = L"abc"; + wil::zwstring_view fromString(stlString); + wil::zwstring_view fromPtr(stlString.data()); + + static constexpr wchar_t charArray[] = L"abc"; + constexpr wil::zwstring_view fromArray(charArray); + + static constexpr wchar_t extendedCharArray[] = L"abc\0\0\0\0\0"; + constexpr wil::zwstring_view fromExtendedArray(extendedCharArray); + + wil::zwstring_view copy = fromLiteral; + + REQUIRE(fromLiteral == stlString); + REQUIRE(fromLiteral == fromString); + REQUIRE(fromLiteral == fromArray); + REQUIRE(fromLiteral == fromExtendedArray); + REQUIRE(fromLiteral == copy); + + // Test decay to std::wstring_view + std::wstring_view sv = fromLiteral; + REQUIRE(sv == fromLiteral); + + // Test operator[] + REQUIRE(fromLiteral[0] == L'a'); + REQUIRE(fromLiteral[1] == L'b'); + REQUIRE(fromLiteral[2] == L'c'); + REQUIRE(fromLiteral[3] == L'\0'); + + // Test constructing with no NULL in range + static constexpr wchar_t badCharArray[2][3] = {{ L'a', L'b', L'c' }, { L'a', L'b', L'c' } }; + REQUIRE_CRASH((wil::zwstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) })); + REQUIRE_CRASH((wil::zwstring_view{ badCharArray[0] })); + + // Test constructing with a NULL one character past the valid range, guarding against off-by-one errors + // Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer + static constexpr wchar_t badCharArrayOffByOne[2][3] = {{ L'a', L'b', L'c' }, {}}; + const wil::zwstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0])); + REQUIRE(fromLiteral == fromTerminatedCharArray); + REQUIRE_CRASH((wil::zwstring_view{ badCharArrayOffByOne[0] })); + + // Test constructing from custom string type + CustomNoncopyableString customString; + wil::zwstring_view fromCustomString(customString); + REQUIRE(fromCustomString == (PCWSTR)customString); +} +#endif diff --git a/Externals/WIL/tests/TokenHelpersTests.cpp b/Externals/WIL/tests/TokenHelpersTests.cpp index 39295405bf..c504ceaf53 100644 --- a/Externals/WIL/tests/TokenHelpersTests.cpp +++ b/Externals/WIL/tests/TokenHelpersTests.cpp @@ -38,6 +38,7 @@ TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]") } #endif +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]") { SECTION("Passing a null token") @@ -92,6 +93,7 @@ TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]") #endif } #endif +#endif bool IsImpersonating() { @@ -186,6 +188,7 @@ TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]") } #endif // WIL_ENABLE_EXCEPTIONS +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) template ::FixedSize>* = nullptr> void TestGetTokenInfoForCurrentThread() { @@ -251,6 +254,7 @@ TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLeve RevertToSelf(); } +#endif bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right) { @@ -274,6 +278,7 @@ TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]") REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS); } +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]") { bool member; @@ -318,3 +323,4 @@ TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]") REQUIRE_NOTHROW(wil::get_token_information()); } #endif // WIL_ENABLE_EXCEPTIONS +#endif diff --git a/Externals/WIL/tests/TraceLoggingTests.cpp b/Externals/WIL/tests/TraceLoggingTests.cpp new file mode 100644 index 0000000000..257480306b --- /dev/null +++ b/Externals/WIL/tests/TraceLoggingTests.cpp @@ -0,0 +1,3 @@ +#include + +// Just verify that Tracelogging.h compiles. \ No newline at end of file diff --git a/Externals/WIL/tests/WatcherTests.cpp b/Externals/WIL/tests/WatcherTests.cpp index aac41c6dae..994c99eab1 100644 --- a/Externals/WIL/tests/WatcherTests.cpp +++ b/Externals/WIL/tests/WatcherTests.cpp @@ -91,7 +91,7 @@ TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]") int volatile countObserved = 0; auto watcher = wil::make_event_watcher_nothrow([&] { - countObserved++; + countObserved = countObserved + 1; notificationReceived.SetEvent(); }); REQUIRE(watcher != nullptr); @@ -125,7 +125,7 @@ TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watch auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&] { allChangesMade.wait(); - countObserved++; + countObserved = countObserved + 1; lastObservedState = stateToObserve; processedChange.SetEvent(); }); @@ -213,7 +213,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]" auto volatile observedChangeType = wil::RegistryChangeKind::Delete; auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) { - countObserved++; + countObserved = countObserved + 1; observedChangeType = changeType; notificationReceived.SetEvent(); }); @@ -252,7 +252,7 @@ TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry called = true; allChangesMade.wait(); - countObserved++; + countObserved = countObserved + 1; lastObservedState = stateToObserve; DWORD value, cbValue = sizeof(value); RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue); @@ -287,7 +287,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_wat auto volatile observedChangeType = wil::RegistryChangeKind::Modify; auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) { - countObserved++; + countObserved = countObserved + 1; observedChangeType = changeType; notificationReceived.SetEvent(); }); @@ -318,7 +318,7 @@ TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_wa } // Stress test, disabled by default -TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]") +TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[LocalOnly][registry][registry_watcher][stress]") { for (DWORD value = 0; value < 10000; ++value) { @@ -355,12 +355,12 @@ TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_w auto volatile observedChangeType = wil::RegistryChangeKind::Modify; wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) { - countObserved++; + countObserved = countObserved + 1; observedChangeType = changeType; notificationReceived.SetEvent(); watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType) { - countObserved++; + countObserved = countObserved + 1; observedChangeType = changeType; notificationReceived.SetEvent(); }); @@ -394,7 +394,7 @@ TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry] notificationReceived.SetEvent(); // ensure that the callback is still being executed while the watcher is reset(). deleteNotification.wait(200); - deleteObserved++; + deleteObserved = deleteObserved + 1; notificationReceived.SetEvent(); }); diff --git a/Externals/WIL/tests/WinRTTests.cpp b/Externals/WIL/tests/WinRTTests.cpp index da53ae430e..4e1ecf2a70 100644 --- a/Externals/WIL/tests/WinRTTests.cpp +++ b/Externals/WIL/tests/WinRTTests.cpp @@ -1,10 +1,10 @@ -#include // TODO: https://github.com/microsoft/wil/issues/44 #include #ifdef WIL_ENABLE_EXCEPTIONS #include #include +#include #endif // Required for pinterface template specializations that we depend on in this test @@ -240,8 +240,8 @@ void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const DoHStringComparisonTest(lhsUniqueStr, rhsUniqueStr, 1); #ifdef WIL_ENABLE_EXCEPTIONS - std::wstring lhsWstr(lhs, 7); - std::wstring rhsWstr(rhs, 7); + std::wstring lhsWstr(lhs); + std::wstring rhsWstr(rhs); DoHStringComparisonTest(lhsWstr, rhsWstr, 1); DoHStringComparisonTest(lhsWstr, rhs, 1); DoHStringComparisonTest(lhsWstr, rhsNonConstArray, 1); @@ -641,7 +641,7 @@ TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]") REQUIRE(time1.UniversalTime == time2.UniversalTime); } -ComPtr> MakeSampleInspectableVector() +ComPtr> MakeSampleInspectableVector(UINT32 count = 5) { auto result = Make>(); REQUIRE(result); @@ -649,7 +649,7 @@ ComPtr> MakeSampleInspectableVector() ComPtr propStatics; REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics)); - for (UINT32 i = 0; i < 5; ++i) + for (UINT32 i = 0; i < count; ++i) { ComPtr myProp; REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp)); @@ -673,12 +673,12 @@ ComPtr> MakeSampleStringVector() return result; } -ComPtr> MakeSamplePointVector() +ComPtr> MakeSamplePointVector(int count = 5) { auto result = Make>(); REQUIRE(result); - for (int i = 0; i < 5; ++i) + for (int i = 0; i < count; ++i) { auto value = static_cast(i); REQUIRE_SUCCEEDED(result->Append(Point{ value, value })); @@ -687,6 +687,33 @@ ComPtr> MakeSamplePointVector() return result; } +template auto cast_to(ComPtr const& src) +{ + ComPtr> theRef; + T value{}; + THROW_IF_FAILED(src.As(&theRef)); + THROW_IF_FAILED(theRef->get_Value(&value)); + return value; +} + +TEST_CASE("WinRTTests::VectorToVectorTest", "[winrt][to_vector]") +{ +#if defined(WIL_ENABLE_EXCEPTIONS) + auto uninit = wil::RoInitialize_failfast(); + auto ints = MakeSampleInspectableVector(100); + auto vec = wil::to_vector(ints.Get()); + UINT32 size; + THROW_IF_FAILED(ints->get_Size(&size)); + REQUIRE(size == vec.size()); + for (UINT32 i = 0; i < size; ++i) + { + ComPtr oneItem; + THROW_IF_FAILED(ints->GetAt(i, &oneItem)); + REQUIRE(cast_to(vec[i]) == cast_to(oneItem)); + } +#endif +} + TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]") { auto uninit = wil::RoInitialize_failfast(); diff --git a/Externals/WIL/tests/WinVerifyTrustTest.cpp b/Externals/WIL/tests/WinVerifyTrustTest.cpp new file mode 100644 index 0000000000..b132d4ae56 --- /dev/null +++ b/Externals/WIL/tests/WinVerifyTrustTest.cpp @@ -0,0 +1,61 @@ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "common.h" + +#pragma comment(lib, "Wintrust.lib") + +TEST_CASE("WilWintrustWrapperTest::VerifyWintrustDataAllocateAndFree", "[resource][wintrust]") +{ + wil::unique_wintrust_data uwvtData; + uwvtData.cbStruct = sizeof(WINTRUST_DATA); + DWORD zero = 0; + REQUIRE(sizeof(WINTRUST_DATA) == uwvtData.cbStruct); + + uwvtData.reset(); + REQUIRE(zero == uwvtData.cbStruct); +} + +TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATADMINAllocateAndFree", "[resource][wintrust]") +{ + wil::unique_hcatadmin hCatAdmin; + + REQUIRE( + CryptCATAdminAcquireContext2( + hCatAdmin.addressof(), + nullptr, + BCRYPT_SHA256_ALGORITHM, + nullptr, + 0)); + + REQUIRE(hCatAdmin.get() != nullptr); + hCatAdmin.reset(); + REQUIRE(hCatAdmin.get() == nullptr); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +TEST_CASE("WilWintrustWrapperTest::VerifyUnqiueHCATINFOAllocate", "[resource][wintrust]") +{ + wil::shared_hcatadmin hCatAdmin; + HCATINFO hCatInfo = nullptr; + + REQUIRE( + CryptCATAdminAcquireContext2( + hCatAdmin.addressof(), + nullptr, + BCRYPT_SHA256_ALGORITHM, + nullptr, + 0)); + + wil::unique_hcatinfo hCatInfoWrapper(hCatInfo, hCatAdmin); + REQUIRE(hCatInfoWrapper.get() == nullptr); +} +#endif diff --git a/Externals/WIL/tests/app/CMakeLists.txt b/Externals/WIL/tests/app/CMakeLists.txt index 1118f23535..97a412e36a 100644 --- a/Externals/WIL/tests/app/CMakeLists.txt +++ b/Externals/WIL/tests/app/CMakeLists.txt @@ -1,21 +1,13 @@ -project(witest.app) add_executable(witest.app) -add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP) +target_compile_definitions(witest.app PRIVATE + -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP + ) -target_sources(witest.app PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp +target_sources(witest.app PRIVATE + ${COMMON_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp ) diff --git a/Externals/WIL/tests/catch.hpp b/Externals/WIL/tests/catch.hpp index 1850fff125..9b309bddc6 100644 --- a/Externals/WIL/tests/catch.hpp +++ b/Externals/WIL/tests/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.7.0 - * Generated: 2019-03-07 21:34:30.252164 + * Catch v2.13.10 + * Generated: 2022-10-16 11:01:23.452308 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 7 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 10 #ifdef __clang__ # pragma clang system_header @@ -66,13 +66,16 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX @@ -132,30 +135,51 @@ namespace Catch { #endif -#if defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + #endif -#ifdef __clang__ +#if defined(__clang__) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ @@ -180,6 +204,7 @@ namespace Catch { // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// @@ -204,20 +229,16 @@ namespace Catch { // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#ifdef _MSC_VER - -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif +#if defined(_MSC_VER) // Universal Windows platform does not support SEH // Or console colours (or console at all...) @@ -227,13 +248,25 @@ namespace Catch { # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif +# if !defined(__clang__) // Handle Clang masquerading for msvc + // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// @@ -266,40 +299,56 @@ namespace Catch { #endif //////////////////////////////////////////////////////////////////////////////// -// Check if string_view is available and usable -// The check is split apart to work around v140 (VS2015) preprocessor issue... -#if defined(__has_include) -#if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE #endif -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable +// Various stdlib support checks that require __has_include #if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 -# include -# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER @@ -324,10 +373,6 @@ namespace Catch { # define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif @@ -336,6 +381,10 @@ namespace Catch { # define CATCH_CONFIG_CPP17_VARIANT #endif +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif @@ -352,17 +401,53 @@ namespace Catch { # define CATCH_CONFIG_POLYFILL_ISNAN #endif +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -427,7 +512,7 @@ namespace Catch { SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - bool empty() const noexcept; + bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; @@ -468,9 +553,10 @@ namespace Catch { } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -497,6 +583,7 @@ namespace Catch { virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); @@ -509,53 +596,30 @@ namespace Catch { #include #include #include +#include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. + /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; + using const_iterator = const char*; private: - friend struct StringRefTestAccess; - - char const* m_start; - size_type m_size; - - char* m_data = nullptr; - - void takeOwnership(); - static constexpr char const* const s_empty = ""; - public: // construction/ assignment - StringRef() noexcept - : StringRef( s_empty, 0 ) - {} + char const* m_start = s_empty; + size_type m_size = 0; - StringRef( StringRef const& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ) - {} - - StringRef( StringRef&& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ), - m_data( other.m_data ) - { - other.m_data = nullptr; - } + public: // construction + constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; - StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} @@ -565,101 +629,64 @@ namespace Catch { m_size( stdString.size() ) {} - ~StringRef() noexcept { - delete[] m_data; + explicit operator std::string() const { + return std::string(m_start, m_size); } - auto operator = ( StringRef const &other ) noexcept -> StringRef& { - delete[] m_data; - m_data = nullptr; - m_start = other.m_start; - m_size = other.m_size; - return *this; - } - - operator std::string() const; - - void swap( StringRef& other ) noexcept; - public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char; + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } public: // named queries - auto empty() const noexcept -> bool { + constexpr auto empty() const noexcept -> bool { return m_size == 0; } - auto size() const noexcept -> size_type { + constexpr auto size() const noexcept -> size_type { return m_size; } - auto numberOfCharacters() const noexcept -> size_type; + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; - // Returns the current start pointer. - // Note that the pointer can change when if the StringRef is a substring - auto currentData() const noexcept -> char const*; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } }; - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } - } // namespace Catch -inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h -// start catch_type_traits.hpp - - -#include - -namespace Catch{ - -#ifdef CATCH_CPP17_OR_GREATER - template - inline constexpr auto is_unique = std::true_type{}; - - template - inline constexpr auto is_unique = std::bool_constant< - (!std::is_same_v && ...) && is_unique - >{}; -#else - -template -struct is_unique : std::true_type{}; - -template -struct is_unique : std::integral_constant -::value - && is_unique::value - && is_unique::value ->{}; - -#endif -} - -// end catch_type_traits.hpp // start catch_preprocessor.hpp @@ -722,23 +749,170 @@ struct is_unique : std::integral_constant #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else -// MSVC is adding extra space and needs more calls to properly remove () -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) -#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template