Merge pull request #11599 from shuffle2/vs17.5

vs: update to 17.5
This commit is contained in:
Pierre Bourdon
2023-03-13 20:43:48 +01:00
committed by GitHub
69 changed files with 14780 additions and 2513 deletions
+1 -1
View File
@@ -326,7 +326,7 @@ if(MSVC)
# Note: In msbuild build, this gets set by msbuild by default
add_compile_options(/Zc:inline)
# Fix various other non-conformant behaviors
add_compile_options(/Zc:__cplusplus,enumTypes,externConstexpr,preprocessor,throwingNew)
add_compile_options(/Zc:__cplusplus,enumTypes,externConstexpr,preprocessor,templateScope,throwingNew)
# Enforce strict volatile semantics as per ISO C++
add_compile_options(/volatile:iso)
+45 -3
View File
@@ -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
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
# Include the .natvis files
if (MSVC)
target_sources(${PROJECT_NAME} INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/natvis/wil.natvis>")
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}")
+9 -3
View File
@@ -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
+47 -45
View File
@@ -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()
File diff suppressed because it is too large Load Diff
+208 -33
View File
@@ -16,6 +16,13 @@
#include "result.h"
#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available
#if __has_include(<tuple>)
#include <tuple>
#endif
#if __has_include(<type_traits>)
#include <type_traits>
#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<char, 0> tag_com_query;
typedef wistd::integral_constant<char, 1> tag_try_com_query;
typedef wistd::integral_constant<char, 2> tag_com_copy;
typedef wistd::integral_constant<char, 3> tag_try_com_copy;
using tag_com_query = wistd::integral_constant<char, 0>;
using tag_try_com_query = wistd::integral_constant<char, 1>;
using tag_com_copy = wistd::integral_constant<char, 2>;
using tag_try_com_copy = wistd::integral_constant<char, 3>;
class default_query_policy
{
@@ -87,7 +94,7 @@ namespace wil
template <typename T>
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<IInspectable**>(&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<IWeakReference>
{
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<IAgileReference>
{
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<T>::type element_type_reference;
typedef details::query_policy_t<T> query_policy;
using element_type_reference = typename wistd::add_lvalue_reference<T>::type;
using query_policy = details::query_policy_t<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<IUnknown**>(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 <class U>
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<U>::query(ptr, &m_ptr));
}
@@ -875,14 +881,12 @@ namespace wil
}
template <class U>
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<U>::query(ptr, &m_ptr));
return;
}
m_ptr = nullptr;
}
template <class U>
@@ -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 <typename T>
com_ptr<T> 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 <typename T>
com_ptr_nothrow<T> 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 <typename T>
com_ptr_failfast<T> 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<T>(ptrSource));
auto hr = details::query_policy_t<decltype(raw)>::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<T>(ptrSource));
auto hr = details::query_policy_t<decltype(raw)>::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<T>(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<T>(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<typename Interface = IUnknown>
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
{
return CoCreateInstance<Interface, err_failfast_policy>(rclsid, dwClsContext);
}
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */
template<typename Class, typename Interface = IUnknown>
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
{
return CoCreateInstanceFailFast<Interface>(__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<typename Interface = IUnknown>
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
{
return CoCreateInstance<Interface, err_returncode_policy>(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<typename Class, typename Interface = IUnknown>
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
{
return CoCreateInstanceNoThrow<Interface>(__uuidof(Class), dwClsContext);
}
@@ -1949,6 +1990,140 @@ namespace wil
{
return CoGetClassObjectNoThrow<Interface>(__uuidof(Class), dwClsContext);
}
#if __cpp_lib_apply && __has_include(<type_traits>)
namespace details
{
template <typename error_policy, typename... Results>
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<wil::com_ptr_t<Results, error_policy>...> resultTuple;
std::apply([i = 0, &multiQis](auto&... a) mutable
{
(a.attach(reinterpret_cast<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
}, resultTuple);
return std::tuple<HRESULT, decltype(resultTuple)>(hr, std::move(resultTuple));
}
template<typename error_policy, typename... Results>
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<wil::com_ptr_t<Results, error_policy>...> resultTuple{};
wil::com_ptr_nothrow<IMultiQI> 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<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
}, resultTuple);
}
return std::tuple<HRESULT, decltype(resultTuple)>{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 <typename... Results>
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
{
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
THROW_IF_FAILED(error);
THROW_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
return result;
}
template <typename... Results>
auto TryCoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
{
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
return result;
}
#endif
// Returns [error, result] where result is a tuple with each of the requested interfaces.
template <typename... Results>
auto CoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
{
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
if (SUCCEEDED(error) && (error == CO_S_NOTALLINTERFACES))
{
return std::tuple<HRESULT, decltype(result)>{E_NOINTERFACE, {}};
}
return std::tuple<HRESULT, decltype(result)>{error, result};
}
template <typename... Results>
auto TryCoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
{
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
return result;
}
template <typename... Results>
auto CoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
{
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
FAIL_FAST_IF_FAILED(error);
FAIL_FAST_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
return result;
}
template <typename... Results>
auto TryCoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
{
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
return result;
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename... Results>
auto com_multi_query(IUnknown* obj)
{
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
THROW_IF_FAILED(error);
THROW_HR_IF(E_NOINTERFACE, error == S_FALSE);
return result;
}
template<typename... Results>
auto try_com_multi_query(IUnknown* obj)
{
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
return result;
}
#endif
#endif // __cpp_lib_apply && __has_include(<type_traits>)
#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)
{
+467
View File
@@ -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 <any>
#include <objidl.h>
#include <roapi.h>
#include <type_traits>
#include <unordered_map>
#include <winrt/Windows.Foundation.h>
#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<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&::RoUnregisterForApartmentShutdown), ::RoUnregisterForApartmentShutdown>;
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<PCWSTR>(moduleAddress), &moduleHandle) != FALSE;
}
struct any_maker_base
{
std::any(*adapter)(void*);
void* inner;
std::any operator()() const
{
return adapter(inner);
}
};
template<typename T>
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<T(*)()>(maker)(); };
inner = reinterpret_cast<void*>(maker);
}
template<typename F>
any_maker(F&& f)
{
adapter = [](auto maker) -> std::any { return reinterpret_cast<F*>(maker)[0](); };
inner = std::addressof(f);
}
};
template<apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
typename test_hook = apartment_variable_platform>
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<apartment_variable_base<leak_action, test_hook>*, std::any> variables;
};
// Apartment id -> variables storage.
inline static wil::object_without_destructor_on_shutdown<
std::unordered_map<unsigned long long, apartment_variable_storage>>
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<T, wil::apartment_variable_leak_action::ignore>
//
// For DLLs, if this is expected, disable this fail fast using
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
//
// 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<ApartmentObserver, IApartmentShutdown>
{
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<ApartmentObserver>().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<typename T>
std::any& get_or_create(any_maker<T> && 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<winrt::apartment_context> 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<typename T, apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
typename test_hook = wil::apartment_variable_platform>
struct apartment_variable : details::apartment_variable_base<leak_action, test_hook>
{
using base = details::apartment_variable_base<leak_action, test_hook>;
constexpr apartment_variable() = default;
// Get current value or throw if no value has been set.
T& get_existing() { return std::any_cast<T&>(base::get_existing()); }
// Get current value or default-construct one on demand.
T& get_or_create()
{
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>()));
}
// Get current value or custom-construct one on demand.
template<typename F>
T& get_or_create(F&& f)
{
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>(std::forward<F>(f))));
}
// get pointer to current value or nullptr if no value has been set
T* get_if() { return std::any_cast<T>(base::get_if()); }
// replace or create the current value, fail fasts if the value is not already stored
template<typename V> void set(V&& value) { return base::set(std::forward<V>(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
+149 -98
View File
@@ -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 <sal.h>
#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<wistd::is_class<ptrType>::value, void*> = (void*)0
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::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<wistd::is_class<ptrType>::value, void*> = nullptr
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::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>(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<HRESULT>(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 <typename T>
_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<T, long>::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<NTSTATUS>(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0));
~~~~
@param status The NTSTATUS returning expression
@return An NTSTATUS representing the evaluation of `val`. */
template <typename T>
_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<T, long>::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 <typename T>
_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<T, long>::value || wistd::is_same<T, unsigned long>::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 <typename T>
struct variable_size_mapping
{
typedef typename variable_size<sizeof(T)>::type type;
using type = typename variable_size<sizeof(T)>::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 <typename T>
using integral_from_enum = typename details::variable_size_mapping<T>::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)
+165 -8
View File
@@ -14,6 +14,7 @@
#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <inspectable.h>
#include <hstring.h>
// 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<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__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<FailureType::Log>(__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<HSTRING>(winrt::get_abi(object));
}
inline auto str_raw_ptr(const winrt::hstring& str) noexcept
{
return str.c_str();
}
template <typename T>
auto put_abi(T& object) noexcept
{
@@ -246,6 +292,117 @@ namespace wil
{
return reinterpret_cast<HSTRING*>(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 <typename T>
T convert_from_abi(::IUnknown* from)
{
T to{ nullptr }; // `T` is a projected type.
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
return to;
}
// For obtaining an object from an interop method on the factory. Example:
// winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);
// If the method produces something different from the factory type:
// winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);
template<typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
auto interop = winrt::get_activation_factory<WinRTFactory, Interface>();
return winrt::capture<WinRTResult>(interop, method, std::forward<Args>(args)...);
}
// For obtaining an object from an interop method on an instance. Example:
// winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);
template<typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
return winrt::capture<WinRTResult>(o.as<Interface>(), method, std::forward<Args>(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<NonWinrtObject>();
~~~~
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<NonWinrtObject>();
~~~~
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
+352
View File
@@ -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<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.RunAsync(std::forward<Args>(args)...);
}
};
struct dispatcher_TryEnqueue
{
template<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.TryEnqueue(std::forward<Args>(args)...);
}
};
template<typename Dispatcher> struct dispatcher_traits;
}
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#include <experimental/coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::experimental::coroutine_handle<T>;
}
#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)
#include <coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::coroutine_handle<T>;
}
#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 <winrt/Namespace.h> 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<typename Dispatcher>
[[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher,
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
{
using Traits = details::dispatcher_traits<Dispatcher>;
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<winrt::hresult>(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<winrt::Windows::UI::Core::CoreDispatcher>
{
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<winrt::Windows::System::DispatcherQueue>
{
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<winrt::Microsoft::System::DispatcherQueue>
{
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<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
{
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<typename T> struct is_winrt_vector_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(),
winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};
template<typename T> struct is_winrt_iterator_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};
template<typename T> constexpr T empty() noexcept
{
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
{
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<winrt::hstring> collection = GetCollection();
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
for (winrt::hstring const& item : allData)
{
// use item
}
~~~
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, 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<typename TSrc> auto to_vector(TSrc const& src)
{
if constexpr (details::is_winrt_vector_like<TSrc>::value)
{
using T = decltype(src.GetAt(0));
std::vector<T> result;
if (auto expected = src.Size())
{
result.resize(expected + 1, details::empty<T>());
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<TSrc>::value)
{
using T = decltype(src.Current());
std::vector<T> result;
constexpr uint32_t chunkSize = 64;
while (true)
{
auto const lastSize = result.size();
result.resize(lastSize + chunkSize, details::empty<T>());
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 <winrt/Windows.UI.h>
//! <Windows.UI.Interop.h> 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
+74
View File
@@ -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 <winrt\base.h>
#include "result_macros.h"
#include <wrl\module.h>
// 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 <typename TCppWinRTClass>
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 <typename TCppWinRTClass>
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<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
}
CATCH_RETURN()
};
}
#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)
#endif // __WIL_CPPWINRT_WRL_INCLUDED
+166 -26
View File
@@ -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<wchar_t[]> 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<details::folder_change_reader_state *>(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<FILE_NOTIFY_INFORMATION *>(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
+168
View File
@@ -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<FailureType>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
template<FailureType>
__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<FailureType::Return>(__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<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
}
#endif
}
template<FailureType T>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}
template<FailureType T>
__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<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(__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<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(__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<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}
}
}
#endif // __WIL_NT_RESULTMACROS_INCLUDED
+1230 -134
View File
File diff suppressed because it is too large Load Diff
+22 -19
View File
@@ -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<DWORD>(sizeof(ProcessLocalStorageData<T>));
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<ProcessLocalStorageData<T>*>(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<T>** data)
static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, _Outptr_result_nullonfailure_ ProcessLocalStorageData<T>** data)
{
*data = nullptr;
const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));
unique_process_heap_ptr<ProcessLocalStorageData<T>> dataAlloc(static_cast<ProcessLocalStorageData<T>*>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size)));
unique_process_heap_ptr<ProcessLocalStorageData<T>> dataAlloc(static_cast<ProcessLocalStorageData<T>*>(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<Node *>(::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<ThreadLocalFailureInfo *>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo)));
errors = reinterpret_cast<ThreadLocalFailureInfo *>(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<FailureType::Log>(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All);
const HRESULT hr = details::ReportFailure_CaughtExceptionCommon<FailureType::Log>(__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).
File diff suppressed because it is too large Load Diff
+32 -3
View File
@@ -31,7 +31,6 @@
#include "com.h"
#include <roerrorapi.h>
#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<IRestrictedErrorInfo> 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
+70 -70
View File
@@ -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<int, char> = IntToChar;
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = 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<int, char> = IntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
}
// Unsafe conversion where failure results in fail fast.
+84 -12
View File
@@ -15,19 +15,17 @@
#include "resource.h"
#include <memory>
#include <string>
#include <vector>
#include <utility>
#if _HAS_CXX17
#include <string_view>
#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 _Ty, class _Alloc>
class vector;
template<class _Elem>
struct char_traits;
template<class _Elem, class _Traits, class _Alloc>
class basic_string;
} // namespace std
namespace wil
{
@@ -42,7 +40,7 @@ namespace wil
template<typename Other>
struct rebind
{
typedef secure_allocator<Other> other;
using other = secure_allocator<Other>;
};
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 TChar>
class basic_zstring_view : public std::basic_string_view<TChar>
{
using size_type = typename std::basic_string_view<TChar>::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<TChar>(pStringData, stringLength)
{
if (pStringData[stringLength] != 0) { WI_STL_FAIL_FAST_IF(true); }
}
template<size_t stringArrayLength>
constexpr basic_zstring_view(const TChar(&stringArray)[stringArrayLength]) noexcept
: std::basic_string_view<TChar>(&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<typename TPtr, std::enable_if_t<
std::is_convertible<TPtr, const TChar*>::value && !std::is_array<TPtr>::value>* = nullptr>
constexpr basic_zstring_view(TPtr&& pStr) noexcept
: std::basic_string_view<TChar>(std::forward<TPtr>(pStr)) {}
constexpr basic_zstring_view(const std::basic_string<TChar>& str) noexcept
: std::basic_string_view<TChar>(&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<TChar> 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<TChar>::swap;
using std::basic_string_view<TChar>::remove_suffix;
};
using zstring_view = basic_zstring_view<char>;
using zwstring_view = basic_zstring_view<wchar_t>;
#endif // _HAS_CXX17
} // namespace wil
#endif // WIL_ENABLE_EXCEPTIONS
+26 -21
View File
@@ -21,7 +21,9 @@
#include <processthreadsapi.h>
// for GetUserNameEx()
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif
#include <Security.h>
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<typename T> struct MapTokenStructToInfoClass;
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenUser; static constexpr bool FixedSize = false; };
// fixed size cases
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenSource; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenType; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { 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<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)

Some files were not shown because too many files have changed in this diff Show More