update wil to 0b2d6c2d822bb301e7558a14ee66d567c14f5dc7

This commit is contained in:
Shawn Hoffman 2023-02-22 13:12:56 -08:00
parent 95ce41ac56
commit 69c335ca8c
66 changed files with 14776 additions and 2507 deletions

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}")

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

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()

3359
Externals/WIL/include/wil/Tracelogging.h vendored Normal file

File diff suppressed because it is too large Load Diff

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)
{

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

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)

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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.

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

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)

View File

@ -0,0 +1,71 @@
#pragma once
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TRACELOGGING_CONFIG_H
#define __WIL_TRACELOGGING_CONFIG_H
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
// in this file configures the provider as a normal (non-telemetry) provider.
#define TraceLoggingOptionMicrosoftTelemetry() \
// Empty definition for TraceLoggingOptionMicrosoftTelemetry
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
// in this file configures the provider as a normal (non-telemetry) provider.
#define TraceLoggingOptionWindowsCoreTelemetry() \
// Empty definition for TraceLoggingOptionWindowsCoreTelemetry
// Event privacy tags. Use the PDT macro values for the tag parameter, e.g.:
// TraceLoggingWrite(...,
// TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage),
// ...);
#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags")
#define PDT_BrowsingHistory 0x0000000000000002u
#define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u
#define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u
#define PDT_ProductAndServicePerformance 0x0000000001000000u
#define PDT_ProductAndServiceUsage 0x0000000002000000u
#define PDT_SoftwareSetupAndInventory 0x0000000080000000u
// Event categories specified via keywords, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
// ...);
#define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47
#define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46
#define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45
#define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment)
// Event categories specified via event tags, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY),
// ...);
#define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000
#define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000
#define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000
#define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000
#define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000
#define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000
#define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000
#define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000
#define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000
#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000
#define MICROSOFT_EVENTTAG_DROP_PII 0x02000000
#define MICROSOFT_EVENTTAG_HASH_PII 0x04000000
#define MICROSOFT_EVENTTAG_MARK_PII 0x08000000
// Field categories specified via field tags, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII),
// ...);
#define MICROSOFT_FIELDTAG_DROP_PII 0x04000000
#define MICROSOFT_FIELDTAG_HASH_PII 0x08000000
#endif // __WIL_TRACELOGGING_CONFIG_H

View File

@ -15,33 +15,177 @@
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
#include <libloaderapi.h> // GetProcAddress
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
#include <PathCch.h>
#include <winreg.h>
#include <objbase.h>
// detect std::bit_cast
#ifdef __has_include
# if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include(<bit>)
# include <bit>
# endif
#endif
#if __cpp_lib_bit_cast >= 201806L
# define __WI_CONSTEXPR_BIT_CAST constexpr
#else
# define __WI_CONSTEXPR_BIT_CAST inline
#endif
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_)
// If we're using c++20, then <compare> must be included to use the string ordinal functions
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
#elif !_HAS_CXX20 && defined(_STRING_VIEW_)
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
#endif
namespace wistd
{
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
#if _HAS_CXX20
using weak_ordering = std::weak_ordering;
#else // _HAS_CXX20
struct weak_ordering
{
static const weak_ordering less;
static const weak_ordering equivalent;
static const weak_ordering greater;
[[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value == 0;
}
[[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value != 0;
}
[[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value < 0;
}
[[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value > 0;
}
[[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value <= 0;
}
[[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value >= 0;
}
[[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept
{
return right == 0;
}
[[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept
{
return right != 0;
}
[[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept
{
return right > 0;
}
[[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept
{
return right < 0;
}
[[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept
{
return right >= 0;
}
[[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept
{
return right <= 0;
}
signed char m_value;
};
inline constexpr weak_ordering weak_ordering::less{static_cast<signed char>(-1)};
inline constexpr weak_ordering weak_ordering::equivalent{static_cast<signed char>(0)};
inline constexpr weak_ordering weak_ordering::greater{static_cast<signed char>(1)};
#endif // !_HAS_CXX20
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
}
namespace wil
{
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
//! CDFs has a limit of 254.
size_t const max_path_segment_length = 255;
constexpr size_t max_path_segment_length = 255;
//! Character length not including the null, MAX_PATH (260) includes the null.
size_t const max_path_length = 259;
constexpr size_t max_path_length = 259;
//! 32743 Character length not including the null. This is a system defined limit.
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
//! It will be 25 when there are more than 9 disks.
size_t const max_extended_path_length = 0x7FFF - 24;
constexpr size_t max_extended_path_length = 0x7FFF - 24;
//! For {guid} string form. Includes space for the null terminator.
size_t const guid_string_buffer_length = 39;
constexpr size_t guid_string_buffer_length = 39;
//! For {guid} string form. Not including the null terminator.
size_t const guid_string_length = 38;
constexpr size_t guid_string_length = 38;
#pragma region String and identifier comparisons
// Using CompareStringOrdinal functions:
//
// Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, etc).
// DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT).
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
namespace details
{
[[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
{
// Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative,
// merely truncating the string. CompareStringOrdinal should be resilient to negatives.
return ::CompareStringOrdinal(left.data(), static_cast<int>(left.size()), right.data(), static_cast<int>(right.size()), caseInsensitive);
}
}
[[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
{
switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive))
{
case CSTR_LESS_THAN:
return wistd::weak_ordering::less;
case CSTR_GREATER_THAN:
return wistd::weak_ordering::greater;
default:
return wistd::weak_ordering::equivalent;
}
}
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
#pragma endregion
#pragma region FILETIME helpers
// FILETIME duration values. FILETIME is in 100 nanosecond units.
@ -56,36 +200,126 @@ namespace wil
namespace filetime
{
inline unsigned long long to_int64(const FILETIME &ft)
constexpr unsigned long long to_int64(const FILETIME &ft) WI_NOEXCEPT
{
#if __cpp_lib_bit_cast >= 201806L
return std::bit_cast<unsigned long long>(ft);
#else
// Cannot reinterpret_cast FILETIME* to unsigned long long*
// due to alignment differences.
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
#endif
}
inline FILETIME from_int64(unsigned long long i64)
__WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
{
#if __cpp_lib_bit_cast >= 201806L
return std::bit_cast<FILETIME>(i64);
#else
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
return *reinterpret_cast<FILETIME *>(&i64);
#endif
}
inline FILETIME add(_In_ FILETIME const &ft, long long delta)
__WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT
{
return from_int64(to_int64(ft) + delta);
return from_int64(to_int64(ft) + delta100ns);
}
inline bool is_empty(const FILETIME &ft)
constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT
{
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
}
inline FILETIME get_system_time()
inline FILETIME get_system_time() WI_NOEXCEPT
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft;
}
/// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT
{
return time100ns / filetime_duration::one_millisecond;
}
/// Convert time as milliseconds to units of 100 nanoseconds.
constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT
{
return timeMsec * filetime_duration::one_millisecond;
}
#if defined(_APISETREALTIME_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
/// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
///
/// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
///
/// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation.
/// For example
///
/// start = GetTickCount64();
/// hibernate();
/// ...wake from hibernation 30 minutes later...;
/// elapsed = GetTickCount64() - start;
/// // elapsed = 30min
///
/// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
///
/// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter).
/// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT
{
ULONGLONG now{};
QueryUnbiasedInterruptTime(&now);
return now;
}
/// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
/// @see QueryUnbiasedInterruptTimeAs100ns
inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT
{
return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
}
#endif // _APISETREALTIME_
}
#pragma endregion
#pragma region RECT helpers
template<typename rect_type>
constexpr auto rect_width(rect_type const& rect)
{
return rect.right - rect.left;
}
template<typename rect_type>
constexpr auto rect_height(rect_type const& rect)
{
return rect.bottom - rect.top;
}
template<typename rect_type>
constexpr auto rect_is_empty(rect_type const& rect)
{
return (rect.left >= rect.right) || (rect.top >= rect.bottom);
}
template<typename rect_type, typename point_type>
constexpr auto rect_contains_point(rect_type const& rect, point_type const& point)
{
return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom);
}
template<typename rect_type, typename length_type>
constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height)
{
rect_type rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
return rect;
}
#pragma endregion
@ -95,7 +329,7 @@ namespace wil
// Adjust stackBufferLength based on typical result sizes to optimize use and
// to test the boundary cases.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback) WI_NOEXCEPT
{
details::string_maker<string_type> maker;
@ -112,14 +346,24 @@ namespace wil
else
{
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
// valueLengthNeededWithNull includes the null so subtract that as make() will add space for it.
RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
// May need to loop more than once if external conditions cause the value to change.
size_t bufferLength;
do
{
bufferLength = valueLengthNeededWithNull;
// bufferLength includes the null so subtract that as make() will add space for it.
RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
size_t secondLength{};
RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
WI_ASSERT(valueLengthNeededWithNull > 0);
// Ensure callback produces consistent result.
FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
// If the value shrunk, then adjust the string to trim off the excess buffer.
if (valueLengthNeededWithNull < bufferLength)
{
RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
}
}
while (valueLengthNeededWithNull > bufferLength);
}
result = maker.release();
return S_OK;
@ -167,9 +411,6 @@ namespace wil
});
}
// This function does not work beyond the default stack buffer size (255).
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
template <typename string_type, size_t stackBufferLength = 256>
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
{
@ -179,8 +420,9 @@ namespace wil
DWORD lengthToUse = static_cast<DWORD>(valueLength);
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
// On both success or insufficient buffer case, add +1 for the null-terminating character
*valueLengthNeededWithNul = lengthToUse + 1;
// On success, return the amount used; on failure, try doubling
*valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2);
return S_OK;
});
}
@ -201,13 +443,11 @@ namespace wil
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type>
/** Looks up the environment variable 'key' and fails if it is not found. */
template <typename string_type, size_t initialBufferLength = 128>
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult(result,
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
// If the function succeeds, the return value is the number of characters stored in the buffer
@ -232,12 +472,11 @@ namespace wil
});
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type>
/** Looks up the environment variable 'key' and returns null if it is not found. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result);
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
return S_OK;
}
@ -245,68 +484,44 @@ namespace wil
/** Retrieves the fully qualified path for the file containing the specified module loaded
by a given process. Note GetModuleFileNameExW is a macro.*/
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT
{
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
// shorter paths, but avoids iteration through the loop in common cases where paths are less
// than 128 characters.
// wil::max_extended_path_length + 1 (for the null char)
// + 1 (to be certain GetModuleFileNameExW didn't truncate)
size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
details::string_maker<string_type> maker;
for (size_t lengthWithNull = initialBufferLength;
lengthWithNull <= maxExtendedPathLengthWithNull;
lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
// make() adds space for the trailing null
RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
DWORD copiedCount;
size_t valueUsedWithNul;
bool copyFailed;
bool copySucceededWithNoTruncation;
if (process != nullptr)
{
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
// The only way to be sure it didn't truncate is if it didn't need the whole buffer.
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
// The only way to be sure it didn't truncate is if it didn't need the whole buffer. The
// count copied to the buffer includes the nul-character as well.
copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
valueUsedWithNul = copiedCount + 1;
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
}
else
{
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
// and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
// the nul-character
copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
valueUsedWithNul = copiedCount + 1;
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
}
if (copyFailed)
{
RETURN_LAST_ERROR();
}
else if (copySucceededWithNoTruncation)
{
path = maker.release();
return S_OK;
}
RETURN_LAST_ERROR_IF(copyFailed);
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
// When the copy truncated, request another try with more space.
*valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
if (lengthWithNull == maxExtendedPathLengthWithNull)
{
// If we've reached this point, there's no point in trying a larger buffer size.
break;
}
}
return S_OK;
};
// Any path should fit into the maximum max_extended_path_length. If we reached here, something went
// terribly wrong.
FAIL_FAST();
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
}
/** Retrieves the fully qualified path for the file that contains the specified module.
@ -314,7 +529,7 @@ namespace wil
same format that was specified when the module was loaded. Therefore, the path can be a
long or short file name, and can have the prefix '\\?\'. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT
{
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
}
@ -357,50 +572,134 @@ namespace wil
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type = wil::unique_cotaskmem_string>
/** Looks up the environment variable 'key' and fails if it is not found. */
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
return result;
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type = wil::unique_cotaskmem_string>
/** Looks up the environment variable 'key' and returns null if it is not found. */
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetModuleFileNameW(HMODULE module)
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(process, module, result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0)
{
string_type result;
THROW_IF_FAILED((wil::QueryFullProcessImageNameW<string_type, stackBufferLength>(processHandle, flags, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
// Lookup a DWORD value under HKLM\...\Image File Execution Options\<current process name>
inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0)
{
auto filePath = wil::GetModuleFileNameW<wil::unique_cotaskmem_string>();
if (auto lastSlash = wcsrchr(filePath.get(), L'\\'))
{
const auto fileName = lastSlash + 1;
auto keyPath = wil::str_concat<wil::unique_cotaskmem_string>(LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)",
fileName);
DWORD value{}, sizeofValue = sizeof(value);
if (::RegGetValueW(HKEY_LOCAL_MACHINE, keyPath.get(), valueName,
#ifdef RRF_SUBKEY_WOW6464KEY
RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
#else
RRF_RT_REG_DWORD,
#endif
nullptr, &value, &sizeofValue) == ERROR_SUCCESS)
{
return value;
}
}
return defaultValue;
}
// Waits for a debugger to attach to the current process based on registry configuration.
//
// Example:
// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe
// WaitForDebuggerPresent=1
//
// REG_DWORD value of
// missing or 0 -> don't break
// 1 -> wait for the debugger, continue execution once it is attached
// 2 -> wait for the debugger, break here once attached.
inline void WaitForDebuggerPresent(bool checkRegistryConfig = true)
{
for (;;)
{
auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1;
if (configValue == 0)
{
return; // not configured, don't wait
}
if (IsDebuggerPresent())
{
if (configValue == 2)
{
DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller
}
return; // debugger now attached, continue executing
}
Sleep(500);
}
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
#endif
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
the linker provides for every module. This avoids the need for a global HINSTANCE variable
and provides access to this value for static libraries. */
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
inline HINSTANCE GetModuleInstanceHandle() WI_NOEXCEPT { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
// GetModuleHandleExW was added to the app partition in version 22000 of the SDK
#if defined(NTDDI_WIN10_CO) ? \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
// Use this in threads that can outlive the object or API call that created them.
// Without this COM, or the API caller, can unload the DLL, resulting in a crash.
// It is very important that this be the first object created in the thread proc
// as when this runs down the thread exits and no destructors of objects created before
// it will run.
[[nodiscard]] inline auto get_module_reference_for_thread() noexcept
{
HMODULE thisModule{};
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule));
return wil::scope_exit([thisModule]
{
FreeLibraryAndExitThread(thisModule, 0);
});
}
#endif
/// @cond
namespace details
@ -410,19 +709,19 @@ namespace wil
INIT_ONCE& m_once;
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
public:
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
init_once_completer(_In_ INIT_ONCE& once) WI_NOEXCEPT : m_once(once)
{
}
#pragma warning(push)
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
void success()
void success() WI_NOEXCEPT
{
m_flags = 0;
}
#pragma warning(pop)
~init_once_completer()
~init_once_completer() WI_NOEXCEPT
{
::InitOnceComplete(&m_once, m_flags, nullptr);
}

View File

@ -0,0 +1,104 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_RESULTMACROS_INCLUDED
#define __WIL_WIN32_RESULTMACROS_INCLUDED
#include "result_macros.h"
// Helpers for return macros
#define __WIN32_RETURN_WIN32(error, str) __WI_SUPPRESS_4127_S do { const auto __error = (error); if (FAILED_WIN32(__error)) { __R_FN(Return_Win32)(__R_INFO(str) __error); } return __error; } __WI_SUPPRESS_4127_E while ((void)0, 0)
#define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str))
FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr)
{
if (SUCCEEDED(hr))
{
return ERROR_SUCCESS;
}
return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr;
}
//*****************************************************************************
// Macros for returning failures as WIN32 error codes
//*****************************************************************************
// Always returns a known result (WIN32 error code) - always logs failures
#define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error)
#define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr)
// Conditionally returns failures (WIN32 error code) - always logs failures
#define WIN32_RETURN_IF_WIN32_ERROR(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { __WIN32_RETURN_WIN32(__errorRet, #error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_NULL(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
// Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
#define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { return __errorRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
//*****************************************************************************
// Macros to catch and convert exceptions on failure
//*****************************************************************************
// Use these macros *within* a catch (...) block to handle exceptions
#define WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr))
// Use these macros in place of a catch block to handle exceptions
#define WIN32_CATCH_RETURN() catch (...) { WIN32_RETURN_CAUGHT_EXCEPTION(); }
namespace wil
{
//*****************************************************************************
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
//*****************************************************************************
// Win32ErrorFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
// it re-throws and catches the exception to convert it to a WIN32 error code. If an exception is of an unrecognized type
// the function will fail fast.
//
// try
// {
// // Code
// }
// catch (...)
// {
// status = wil::Win32ErrorFromCaughtException();
// }
_Always_(_Post_satisfies_(return > 0))
__declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT
{
return __WIN32_FROM_HRESULT(ResultFromCaughtException());
}
namespace details::__R_NS_NAME
{
#ifdef WIL_ENABLE_EXCEPTIONS
__R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
}
#endif
__R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
}
}
}
#endif // __WIL_WIN32_RESULTMACROS_INCLUDED

View File

@ -19,24 +19,40 @@
#include "result.h"
#include "com.h"
#include "resource.h"
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#ifdef __cplusplus_winrt
#include <collection.h> // bring in the CRT iterator for support for C++ CX code
#endif
#ifdef WIL_ENABLE_EXCEPTIONS
/// @cond
#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WI_HAS_STD_LESS)
#ifdef __has_include
#if __has_include(<functional>)
#define __WI_HAS_STD_LESS 1
#include <functional>
#endif // Otherwise, not using STL; don't specialize std::less
#else
// Fall back to the old way of forward declaring std::less
#define __WI_HAS_STD_LESS 1
#pragma warning(push)
#pragma warning(disable:4643) // Forward declaring '...' in namespace std is not permitted by the C++ Standard.
namespace std
{
template<class _Elem, class _Traits, class _Alloc>
class basic_string;
template<class _Ty>
struct less;
}
/// @endcond
#pragma warning(pop)
#endif
#endif
#if defined(WIL_ENABLE_EXCEPTIONS) && defined(__has_include)
#if __has_include(<vector>)
#define __WI_HAS_STD_VECTOR 1
#include <vector>
#endif
#endif
/// @endcond
// This enables this code to be used in code that uses the ABI prefix or not.
// Code using the public SDK and C++ CX code has the ABI prefix, windows internal
@ -50,10 +66,9 @@ namespace std
namespace wil
{
#ifdef _INC_TIME
// time_t is the number of 1 - second intervals since January 1, 1970.
long long const SecondsToStartOf1970 = 0x2b6109100;
long long const HundredNanoSecondsInSecond = 10000000LL;
constexpr long long SecondsToStartOf1970 = 0x2b6109100;
constexpr long long HundredNanoSecondsInSecond = 10000000LL;
inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime)
{
@ -67,7 +82,6 @@ namespace wil
dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond;
return dateTime;
}
#endif // _INC_TIME
#pragma region HSTRING Helpers
/// @cond
@ -145,16 +159,17 @@ namespace wil
return str;
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<class TraitsT, class AllocT>
static const wchar_t* get_buffer(
const std::basic_string<wchar_t, TraitsT, AllocT>& str,
UINT32* length) WI_NOEXCEPT
// Overload for std::wstring, or at least things that behave like std::wstring, without adding a dependency
// on STL headers
template <typename StringT>
static wistd::enable_if_t<wistd::conjunction_v<
wistd::is_same<const wchar_t*, decltype(wistd::declval<StringT>().c_str())>,
wistd::is_same<typename StringT::size_type, decltype(wistd::declval<StringT>().length())>>,
const wchar_t*> get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT
{
*length = static_cast<UINT32>(str.length());
return str.c_str();
}
#endif
template <typename LhsT, typename RhsT>
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
@ -224,8 +239,8 @@ namespace wil
//! Detects if one or more embedded null is present in an HSTRING.
inline bool HasEmbeddedNull(_In_opt_ HSTRING value)
{
BOOL hasEmbeddedNull;
WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull);
BOOL hasEmbeddedNull = FALSE;
(void)WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull);
return hasEmbeddedNull != FALSE;
}
@ -429,14 +444,14 @@ namespace wil
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
struct type // T holder
{
type() {};
type(T&& value) : m_value(wistd::forward<T>(value)) {};
type() = default;
type(T&& value) : m_value(wistd::forward<T>(value)) {}
operator T() const { return m_value; }
type& operator=(T&& value) { m_value = wistd::forward<T>(value); return *this; }
T Get() const { return m_value; }
// Returning T&& to support move only types
// In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen
// In case of absence of T::operator=(T&&) a call to T::operator=(const T&) will happen
T&& Get() { return wistd::move(m_value); }
HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; }
@ -730,7 +745,7 @@ namespace wil
vector_range_nothrow(const vector_range_nothrow&) = delete;
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
vector_range_nothrow(vector_range_nothrow&& other) :
vector_range_nothrow(vector_range_nothrow&& other) WI_NOEXCEPT :
m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
m_currentElement(wistd::move(other.m_currentElement))
{
@ -985,6 +1000,39 @@ namespace wil
};
#pragma endregion
#if defined(__WI_HAS_STD_VECTOR)
/** Converts WinRT vectors to std::vector by requesting the collection's data in a single
operation. This can be more efficient in terms of IPC cost than iteratively processing it.
~~~
ComPtr<IVector<IPropertyValue*>> values = GetValues();
std::vector<ComPtr<IPropertyValue>> allData = wil::to_vector(values);
for (ComPtr<IPropertyValue> const& item : allData)
{
// use item
}
Can be used for ABI::Windows::Foundation::Collections::IVector<T> and
ABI::Windows::Foundation::Collections::IVectorView<T>
*/
template<typename VectorType> auto to_vector(VectorType* src)
{
using TResult = typename details::MapVectorResultType<VectorType>::type;
using TSmart = typename details::MapToSmartType<TResult>::type;
static_assert(sizeof(TResult) == sizeof(TSmart), "result and smart sizes are different");
std::vector<TSmart> output;
UINT32 expected = 0;
THROW_IF_FAILED(src->get_Size(&expected));
if (expected > 0)
{
output.resize(expected + 1);
UINT32 fetched = 0;
THROW_IF_FAILED(src->GetMany(0, static_cast<UINT32>(output.size()), reinterpret_cast<TResult*>(output.data()), &fetched));
THROW_HR_IF(E_CHANGED_STATE, fetched > expected);
output.resize(fetched);
}
return output;
}
#endif
#pragma region error code base IIterable<>
template <typename T>
class iterable_range_nothrow
@ -998,7 +1046,7 @@ namespace wil
iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete;
iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete;
iterable_range_nothrow(iterable_range_nothrow&& other) :
iterable_range_nothrow(iterable_range_nothrow&& other) WI_NOEXCEPT :
m_iterator(wistd::move(other.m_iterator)), m_element(wistd::move(other.m_element)),
m_resultStorage(other.m_resultStorage)
{
@ -1274,9 +1322,9 @@ namespace details
// LastType<int, bool>::type boolValue;
template <typename... Ts> struct LastType
{
template<typename T, typename... Ts> struct LastTypeOfTs
template<typename T, typename... OtherTs> struct LastTypeOfTs
{
typedef typename LastTypeOfTs<Ts...>::type type;
typedef typename LastTypeOfTs<OtherTs...>::type type;
};
template<typename T> struct LastTypeOfTs<T>
@ -1284,8 +1332,8 @@ namespace details
typedef T type;
};
template<typename... Ts>
static typename LastTypeOfTs<Ts...>::type LastTypeOfTsFunc() {}
template<typename... OtherTs>
static typename LastTypeOfTs<OtherTs...>::type LastTypeOfTsFunc() {}
typedef decltype(LastTypeOfTsFunc<Ts...>()) type;
};
@ -1315,14 +1363,28 @@ namespace details
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
[func = wistd::forward<TFunction>(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT
{
HRESULT hr = S_OK;
if (status != AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully
if (status != ABI::Windows::Foundation::AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully
{
ComPtr<IAsyncInfo> asyncInfo;
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // All must implement IAsyncInfo
asyncInfo->get_ErrorCode(&hr);
// QI to the IAsyncInfo interface. While all operations implement this, it is
// possible that the stub has disconnected, causing the QI to fail.
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
if (SUCCEEDED(hr))
{
// Save the error code result in a temporary variable to allow us
// to also retrieve the result of the COM call. If the stub has
// disconnected, this call may fail.
HRESULT errorCode = E_UNEXPECTED;
hr = asyncInfo->get_ErrorCode(&errorCode);
if (SUCCEEDED(hr))
{
// Return the operations error code to the caller.
hr = errorCode;
}
}
}
return CallAndHandleErrors(func, hr);
@ -1340,21 +1402,35 @@ namespace details
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
[func = wistd::forward<TFunction>(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT
{
typename details::MapToSmartType<typename GetAbiType<typename wistd::remove_pointer<TIOperation>::type::TResult_complex>::type>::type result;
HRESULT hr = S_OK;
if (status == AsyncStatus::Completed)
// avoid a potentially costly marshaled QI / call if we completed successfully
if (status == ABI::Windows::Foundation::AsyncStatus::Completed)
{
hr = operation->GetResults(result.GetAddressOf());
}
else
{
// avoid a potentially costly marshaled QI / call if we completed successfully
ComPtr<IAsyncInfo> asyncInfo;
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this
asyncInfo->get_ErrorCode(&hr);
// QI to the IAsyncInfo interface. While all operations implement this, it is
// possible that the stub has disconnected, causing the QI to fail.
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
if (SUCCEEDED(hr))
{
// Save the error code result in a temporary variable to allow us
// to also retrieve the result of the COM call. If the stub has
// disconnected, this call may fail.
HRESULT errorCode = E_UNEXPECTED;
hr = asyncInfo->get_ErrorCode(&errorCode);
if (SUCCEEDED(hr))
{
// Return the operations error code to the caller.
hr = errorCode;
}
}
}
return CallAndHandleErrors(func, hr, result.Get());
@ -1378,7 +1454,7 @@ namespace details
RETURN_HR(m_completedEventHandle.create());
}
HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, AsyncStatus status) override
HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, ABI::Windows::Foundation::AsyncStatus status) override
{
m_status = status;
m_completedEventHandle.SetEvent();
@ -1390,13 +1466,13 @@ namespace details
return m_completedEventHandle.get();
}
AsyncStatus GetStatus() const
ABI::Windows::Foundation::AsyncStatus GetStatus() const
{
return m_status;
}
private:
volatile AsyncStatus m_status = AsyncStatus::Started;
volatile ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
wil::unique_event_nothrow m_completedEventHandle;
};
@ -1419,12 +1495,25 @@ namespace details
}
RETURN_IF_FAILED(hr);
if (completedDelegate->GetStatus() != AsyncStatus::Completed)
if (completedDelegate->GetStatus() != ABI::Windows::Foundation::AsyncStatus::Completed)
{
Microsoft::WRL::ComPtr<IAsyncInfo> asyncInfo;
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this
hr = E_UNEXPECTED;
asyncInfo->get_ErrorCode(&hr); // error return ignored, ok?
// QI to the IAsyncInfo interface. While all operations implement this, it is
// possible that the stub has disconnected, causing the QI to fail.
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
if (SUCCEEDED(hr))
{
// Save the error code result in a temporary variable to allow us
// to also retrieve the result of the COM call. If the stub has
// disconnected, this call may fail.
HRESULT errorCode = E_UNEXPECTED;
hr = asyncInfo->get_ErrorCode(&errorCode);
if (SUCCEEDED(hr))
{
// Return the operations error code to the caller.
hr = errorCode;
}
}
return hr; // leave it to the caller to log failures.
}
return S_OK;
@ -1669,7 +1758,7 @@ namespace details
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>* competed) override
{
competed->Invoke(this, AsyncStatus::Completed);
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
@ -1708,7 +1797,7 @@ namespace details
public:
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override
{
competed->Invoke(this, AsyncStatus::Completed);
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
return S_OK;
}
@ -1866,7 +1955,19 @@ public:
}
else
{
auto resolvedSender = m_weakSender.Resolve<T>();
auto resolvedSender = [&]()
{
try
{
return m_weakSender.Resolve<T>();
}
catch (...)
{
// Ignore RPC or other failures that are unavoidable in some cases
// matching wil::unique_winrt_event_token and winrt::event_revoker
return static_cast<T^>(nullptr);
}
}();
if (resolvedSender)
{
(resolvedSender->*m_removalFunction)(m_token);
@ -2205,7 +2306,7 @@ struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<ABI
#pragma pop_macro("ABI")
#endif
#ifdef WIL_ENABLE_EXCEPTIONS
#if __WI_HAS_STD_LESS
namespace std
{

View File

@ -39,7 +39,7 @@
#define _WISTD_CONFIG_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include <stddef.h> // For size_t and other necessary types
#include <cstddef> // For size_t and other necessary types
/// @cond
#if defined(_MSC_VER) && !defined(__clang__)
@ -107,6 +107,29 @@
# define __WI_LIBCPP_COMPILER_IBM
#endif
#if defined(__WI_LIBCPP_COMPILER_MSVC)
#define __WI_PUSH_WARNINGS __pragma(warning(push))
#define __WI_POP_WARNINGS __pragma(warning(pop))
#elif defined(__WI_LIBCPP_COMPILER_CLANG)
#define __WI_PUSH_WARNINGS __pragma(clang diagnostic push)
#define __WI_POP_WARNINGS __pragma(clang diagnostic pop)
#else
#define __WI_PUSH_WARNINGS
#define __WI_POP_WARNINGS
#endif
#ifdef __WI_LIBCPP_COMPILER_MSVC
#define __WI_MSVC_DISABLE_WARNING(id) __pragma(warning(disable: id))
#else
#define __WI_MSVC_DISABLE_WARNING(id)
#endif
#ifdef __WI_LIBCPP_COMPILER_CLANG
#define __WI_CLANG_DISABLE_WARNING(warning) __pragma(clang diagnostic ignored #warning)
#else
#define __WI_CLANG_DISABLE_WARNING(warning)
#endif
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
@ -448,7 +471,7 @@
namespace wistd // ("Windows Implementation" std)
{
typedef decltype(__nullptr) nullptr_t;
using nullptr_t = decltype(__nullptr);
template <class _T1, class _T2 = _T1>
struct __less
@ -531,18 +554,18 @@ namespace wistd // ("Windows Implementation" std)
template <class _Arg, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
{
typedef _Arg argument_type;
typedef _Result result_type;
using argument_type = _Arg;
using result_type = _Result;
};
template <class _Arg1, class _Arg2, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
{
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
using first_argument_type = _Arg1;
using second_argument_type = _Arg2;
using result_type = _Result;
};
}
/// @endcond
#endif _WISTD_CONFIG_H_
#endif // _WISTD_CONFIG_H_

View File

@ -46,6 +46,7 @@
#pragma warning(push)
#pragma warning(disable: 4324)
#pragma warning(disable: 4800)
/// @cond
namespace wistd // ("Windows Implementation" std)
@ -249,25 +250,28 @@ namespace wistd // ("Windows Implementation" std)
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
}
} // __function
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
// pointers (__base vtable takes an additional one).
static constexpr size_t __buffer_size = 13 * sizeof(void*);
constexpr const size_t __buffer_size = 13 * sizeof(void*);
typedef __function::__base<_Rp(_ArgTypes...)> __base;
} // __function
// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in
// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type)
function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
using __base = __function::__base<_Rp(_ArgTypes...)>;
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
typename aligned_storage<__buffer_size>::type __buf_;
typename aligned_storage<__function::__buffer_size>::type __buf_;
__base* __f_;
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
return reinterpret_cast<__base*>(p);
return static_cast<__base*>(p);
}
template <class _Fp, bool>
@ -281,7 +285,7 @@ namespace wistd // ("Windows Implementation" std)
template <class _Fp>
struct __callable_imp<_Fp, false>
{
static const bool value = false;
static constexpr bool value = false;
};
template <class _Fp>
@ -296,11 +300,12 @@ namespace wistd // ("Windows Implementation" std)
template <class _Fp>
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
public:
typedef _Rp result_type;
using result_type = _Rp;
// construct/copy/destroy:
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function() WI_NOEXCEPT : __f_(0) {}
__WI_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
function(const function&);
@ -340,7 +345,7 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(const function& __f)
{
if (__f.__f_ == 0)
if (__f.__f_ == nullptr)
__f_ = 0;
else
{
@ -353,7 +358,7 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(function&& __f)
{
if (__f.__f_ == 0)
if (__f.__f_ == nullptr)
__f_ = 0;
else
{
@ -368,14 +373,14 @@ namespace wistd // ("Windows Implementation" std)
template <class _Fp, class>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(_Fp __f)
: __f_(0)
: __f_(nullptr)
{
if (__function::__not_null(__f))
{
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
}
}
@ -429,7 +434,7 @@ namespace wistd // ("Windows Implementation" std)
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
}
return *this;
@ -483,7 +488,7 @@ namespace wistd // ("Windows Implementation" std)
_Rp
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
{
if (__f_ == 0)
if (__f_ == nullptr)
__throw_bad_function_call();
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
}

View File

@ -60,13 +60,13 @@ namespace wistd // ("Windows Implementation" std)
template <class _Tp, class _Dp, bool = __has_pointer_type<_Dp>::value>
struct __pointer_type
{
typedef typename _Dp::pointer type;
using type = typename _Dp::pointer;
};
template <class _Tp, class _Dp>
struct __pointer_type<_Tp, _Dp, false>
{
typedef _Tp* type;
using type = _Tp*;
};
} // __pointer_type_imp
@ -74,16 +74,16 @@ namespace wistd // ("Windows Implementation" std)
template <class _Tp, class _Dp>
struct __pointer_type
{
typedef typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type type;
using type = typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type;
};
template <class _Tp, int _Idx,
bool _CanBeEmptyBase =
is_empty<_Tp>::value && !__libcpp_is_final<_Tp>::value>
struct __compressed_pair_elem {
typedef _Tp _ParamT;
typedef _Tp& reference;
typedef const _Tp& const_reference;
using _ParamT = _Tp;
using reference = _Tp&;
using const_reference = const _Tp&;
#ifndef __WI_LIBCPP_CXX03_LANG
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {}
@ -115,10 +115,10 @@ namespace wistd // ("Windows Implementation" std)
template <class _Tp, int _Idx>
struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp {
typedef _Tp _ParamT;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp __value_type;
using _ParamT = _Tp;
using reference = _Tp&;
using const_reference = const _Tp&;
using __value_type = _Tp;
#ifndef __WI_LIBCPP_CXX03_LANG
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default;
@ -149,10 +149,10 @@ namespace wistd // ("Windows Implementation" std)
struct __second_tag {};
template <class _T1, class _T2>
class __compressed_pair : private __compressed_pair_elem<_T1, 0>,
private __compressed_pair_elem<_T2, 1> {
typedef __compressed_pair_elem<_T1, 0> _Base1;
typedef __compressed_pair_elem<_T2, 1> _Base2;
class __declspec(empty_bases) __compressed_pair : private __compressed_pair_elem<_T1, 0>,
private __compressed_pair_elem<_T2, 1> {
using _Base1 = __compressed_pair_elem<_T1, 0>;
using _Base2 = __compressed_pair_elem<_T2, 1>;
// NOTE: This static assert should never fire because __compressed_pair
// is *almost never* used in a scenario where it's possible for T1 == T2.
@ -271,7 +271,7 @@ namespace wistd // ("Windows Implementation" std)
__WI_LIBCPP_INLINE_VISIBILITY
default_delete(const default_delete<_Up>&,
typename enable_if<is_convertible<_Up*, _Tp*>::value>::type* =
0) WI_NOEXCEPT {}
nullptr) WI_NOEXCEPT {}
__WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT {
static_assert(sizeof(_Tp) > 0,
@ -299,7 +299,7 @@ namespace wistd // ("Windows Implementation" std)
template <class _Up>
__WI_LIBCPP_INLINE_VISIBILITY
default_delete(const default_delete<_Up[]>&,
typename _EnableIfConvertible<_Up>::type* = 0) WI_NOEXCEPT {}
typename _EnableIfConvertible<_Up>::type* = nullptr) WI_NOEXCEPT {}
template <class _Up>
__WI_LIBCPP_INLINE_VISIBILITY
@ -319,32 +319,32 @@ namespace wistd // ("Windows Implementation" std)
template <class _Deleter>
struct __unique_ptr_deleter_sfinae {
static_assert(!is_reference<_Deleter>::value, "incorrect specialization");
typedef const _Deleter& __lval_ref_type;
typedef _Deleter&& __good_rval_ref_type;
typedef true_type __enable_rval_overload;
using __lval_ref_type = const _Deleter&;
using __good_rval_ref_type = _Deleter&&;
using __enable_rval_overload = true_type;
};
template <class _Deleter>
struct __unique_ptr_deleter_sfinae<_Deleter const&> {
typedef const _Deleter& __lval_ref_type;
typedef const _Deleter&& __bad_rval_ref_type;
typedef false_type __enable_rval_overload;
using __lval_ref_type = const _Deleter&;
using __bad_rval_ref_type = const _Deleter&&;
using __enable_rval_overload = false_type;
};
template <class _Deleter>
struct __unique_ptr_deleter_sfinae<_Deleter&> {
typedef _Deleter& __lval_ref_type;
typedef _Deleter&& __bad_rval_ref_type;
typedef false_type __enable_rval_overload;
using __lval_ref_type = _Deleter&;
using __bad_rval_ref_type = _Deleter&&;
using __enable_rval_overload = false_type;
};
#endif // !defined(__WI_LIBCPP_CXX03_LANG)
template <class _Tp, class _Dp = default_delete<_Tp> >
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr {
public:
typedef _Tp element_type;
typedef _Dp deleter_type;
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
using element_type = _Tp;
using deleter_type = _Dp;
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
static_assert(!is_rvalue_reference<deleter_type>::value,
"the specified deleter type cannot be an rvalue reference");
@ -355,7 +355,7 @@ namespace wistd // ("Windows Implementation" std)
struct __nat { int __for_bool_; };
#ifndef __WI_LIBCPP_CXX03_LANG
typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
template <bool _Dummy>
using _LValRefType =
@ -582,9 +582,9 @@ namespace wistd // ("Windows Implementation" std)
template <class _Tp, class _Dp>
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
public:
typedef _Tp element_type;
typedef _Dp deleter_type;
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
using element_type = _Tp;
using deleter_type = _Dp;
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
private:
__compressed_pair<pointer, deleter_type> __ptr_;
@ -602,7 +602,7 @@ namespace wistd // ("Windows Implementation" std)
{};
#ifndef __WI_LIBCPP_CXX03_LANG
typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
template <bool _Dummy>
using _LValRefType =

View File

@ -14,6 +14,11 @@
#include <wrl.h>
#include "result.h"
#include "common.h" // wistd type_traits helpers
#include <libloaderapi.h> // GetModuleHandleW
/// @cond
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
/// @endcond
namespace wil
{
@ -79,6 +84,44 @@ namespace wil
return result;
}
#endif // WIL_ENABLE_EXCEPTIONS
/** Holds a reference to the host WRL module to prevent it from being unloaded.
Normally, the reference is held implicitly because you are a member function
of a DLL-hosted COM object, or because you retain a strong reference
to some DLL-hosted COM object, but if those do not apply to you, then you
will need to hold a reference explicitly. For examples (and for the C++/WinRT
equivalent), see winrt_module_reference.
*/
struct [[nodiscard]] wrl_module_reference
{
wrl_module_reference()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->IncrementObjectCount();
}
else
{
#ifdef GET_MODULE_HANDLE_EX_FLAG_PIN
// If this assertion fails, then you are using wrl_module_reference
// from a DLL that does not host WRL objects, and the module reference
// has no effect.
WI_ASSERT(reinterpret_cast<HMODULE>(&__ImageBase) == GetModuleHandleW(nullptr));
#endif
}
}
wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() {}
~wrl_module_reference()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->DecrementObjectCount();
}
}
};
} // namespace wil
#endif // __WIL_WRL_INCLUDED

114
Externals/WIL/natvis/wil.natvis vendored Normal file
View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="wistd::_Func_impl&lt;*&gt;">
<DisplayString>{_Callee._Object}</DisplayString>
<Expand>
</Expand>
</Type>
<Type Name="wistd::function&lt;*&gt;">
<DisplayString Condition="_Impl == 0">empty</DisplayString>
<DisplayString Condition="_Impl != 0">{*_Impl}</DisplayString>
<Expand>
<ExpandedItem Condition="_Impl != 0">*_Impl</ExpandedItem>
</Expand>
</Type>
<Type Name="wistd::unique_ptr&lt;*&gt;">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString Condition="_Myptr != 0">{*_Myptr}</DisplayString>
<StringView>_Myptr</StringView>
<Expand>
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
</Expand>
</Type>
<Type Name="wistd::unique_ptr&lt;wchar_t[*],*&gt;">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString Condition="_Myptr != 0">{_Myptr,su}</DisplayString>
<StringView>_Myptr</StringView>
<Expand>
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
<Item Name="[length]" Condition="_Myptr != 0">wcslen(_Myptr)</Item>
</Expand>
</Type>
<Type Name="wistd::unique_ptr&lt;char[*],*&gt;">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString Condition="_Myptr != 0">{_Myptr,s}</DisplayString>
<StringView>_Myptr</StringView>
<Expand>
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
<Item Name="[length]" Condition="_Myptr != 0">strlen(_Myptr)</Item>
</Expand>
</Type>
<Type Name="wil::details::shared_storage&lt;*&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{*m_ptr}</DisplayString>
<StringView>m_ptr</StringView>
<Expand>
<Item Name="[pointer]" Condition="m_ptr != 0">m_ptr</Item>
</Expand>
</Type>
<Type Name="wil::details::unique_storage&lt;wil::details::handle_null_resource_policy&lt;*&gt;&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
<Expand>
</Expand>
</Type>
<Type Name="wil::details::unique_storage&lt;wil::details::handle_invalid_resource_policy&lt;*&gt;&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
<Expand>
</Expand>
</Type>
<Type Name="wil::details::unique_storage&lt;wil::details::resource_policy&lt;*&gt;&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
<StringView>m_ptr</StringView>
<Expand>
</Expand>
</Type>
<Type Name="wil::details::unique_storage&lt;wil::details::resource_policy&lt;wchar_t *,*&gt;&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{m_ptr,su}</DisplayString>
<StringView>m_ptr</StringView>
<Expand>
<Item Name="[length]" Condition="m_ptr != 0">wcslen(m_ptr)</Item>
</Expand>
</Type>
<Type Name="wil::details::unique_storage&lt;wil::details::resource_policy&lt;char *,*&gt;&gt;">
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
<DisplayString Condition="m_ptr != 0">{m_ptr,s}</DisplayString>
<StringView>m_ptr</StringView>
<Expand>
<Item Name="[length]" Condition="m_ptr != 0">strlen(m_ptr)</Item>
</Expand>
</Type>
<Type Name="wil::com_ptr_t&lt;*&gt;">
<DisplayString>{m_ptr}</DisplayString>
<Expand>
<ExpandedItem>m_ptr</ExpandedItem>
</Expand>
</Type>
<Type Name="wil::basic_zstring_view&lt;*&gt;">
<Intrinsic Name="size" Expression="_Mysize" />
<Intrinsic Name="data" Expression="_Mydata" />
<DisplayString>{_Mydata,[_Mysize]}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">size()</Item>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -7,6 +7,7 @@ file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})
file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)
file(GLOB_RECURSE wil_natvis ${CMAKE_SOURCE_DIR}/natvis/*.natvis)
add_custom_command(OUTPUT ${wil_nupkg}
COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive
@ -14,6 +15,7 @@ add_custom_command(OUTPUT ${wil_nupkg}
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
${wil_headers}
${wil_natvis}
${CMAKE_SOURCE_DIR}/LICENSE
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)

View File

@ -16,6 +16,7 @@
<file src="..\..\LICENSE"/>
<file src="..\..\ThirdPartyNotices.txt"/>
<file src="..\..\include\**" target="include\" />
<file src="..\..\natvis\wil.natvis" target="natvis\" />
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
</files>
</package>

View File

@ -5,4 +5,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis"/>
</ItemGroup>
</Project>

View File

@ -8,7 +8,7 @@ jobs:
timeoutInMinutes: 360
pool:
vmImage: 'windows-2019'
vmImage: 'windows-2022'
steps:
- script: |
@ -18,7 +18,7 @@ jobs:
displayName: 'Install Clang'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
@ -28,7 +28,7 @@ jobs:
displayName: 'Build x86'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
@ -37,5 +37,9 @@ jobs:
call scripts\build_all.cmd
displayName: 'Build x64'
- script: call scripts\runtests.cmd
# NOTE: We run the tests in the 32-bit cross-tools window out of convenience as this adds all necessary directories to
# the PATH that are necessary for finding the ASan/UBSan DLLs
- script: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat""
call scripts\runtests.cmd ~[LocalOnly]
displayName: 'Run Tests'

View File

@ -1,4 +1,5 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
@ -15,23 +16,15 @@ if "%Platform%"=="x64" (
exit /B 1
)
call :build clang debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
set COMPILERS=clang msvc
set BUILD_TYPES=debug release relwithdebinfo minsizerel
call :build msvc debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%b in (%BUILD_TYPES%) do (
call :build %%c %%b
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)
echo All build completed successfully!
@ -47,5 +40,6 @@ if not exist %BUILD_DIR% (
pushd %BUILD_DIR%
echo Building from %CD%
ninja
set EXIT_CODE=%ERRORLEVEL%
popd
goto :eof
exit /B %EXIT_CODE%

View File

@ -10,8 +10,8 @@ goto :init
:usage
echo USAGE:
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-l^|--linker link^|lld-link]
echo [--fast] [-v^|--version X.Y.Z]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-v^|--version X.Y.Z]
echo [--cppwinrt ^<version^>] [--fast]
echo.
echo ARGUMENTS
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
@ -20,6 +20,7 @@ goto :init
echo 'relwithdebinfo', or 'minsizerel'
echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI
echo build and is typically not necessary when building locally
echo --cppwinrt Manually specifies the version of C++/WinRT to use for generating headers
echo --fast Used to (slightly) reduce compile times and build output size. This is primarily
echo used by the CI build machines where resources are more constrained. This switch is
echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed
@ -30,10 +31,10 @@ goto :init
set COMPILER=
set GENERATOR=
set BUILD_TYPE=
set LINKER=
set CMAKE_ARGS=
set BITNESS=
set VERSION=
set CPPWINRT_VERSION=
set FAST_BUILD=0
:parse
@ -88,29 +89,25 @@ goto :init
goto :parse
)
set LINKER_SET=0
if /I "%~1"=="-l" set LINKER_SET=1
if /I "%~1"=="--linker" set LINKER_SET=1
if %LINKER_SET%==1 (
if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1
set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=1
if %VERSION_SET%==1 (
if "%VERSION%" NEQ "" echo ERROR: Version already specified & call :usage & exit /B 1
if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1
if /I "%~2"=="link" set LINKER=link
if /I "%~2"=="lld-link" set LINKER=lld-link
if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1
set VERSION=%~2
shift
shift
goto :parse
)
set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=1
if %VERSION_SET%==1 (
if "%VERSION%" NEQ "" echo ERROR: Version alread specified & call :usage & exit /B 1
if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1
if /I "%~1"=="--cppwinrt" (
if "%CPPWINRT_VERSION%" NEQ "" echo ERROR: C++/WinRT version already specified & call :usage & exit /B 1
if /I "%~2"=="" echo ERROR: C++/WinRT version string missing & call :usage & exit /B 1
set VERSION=%~2
set CPPWINRT_VERSION=%~2
shift
shift
@ -132,9 +129,6 @@ goto :init
:: Check for conflicting arguments
if "%GENERATOR%"=="msbuild" (
if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
:: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator
if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1
)
:: Select defaults
@ -145,8 +139,6 @@ goto :init
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
if "%LINKER%"=="" set LINKER=link
:: Formulate CMake arguments
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
@ -167,14 +159,14 @@ goto :init
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0
)
if %LINKER%==lld-link (
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link
)
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
if "%CPPWINRT_VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DCPPWINRT_VERSION=%CPPWINRT_VERSION%
if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
:: Figure out the platform
if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1
if "%Platform%"=="x86" (
@ -191,7 +183,6 @@ goto :init
:: Run CMake
pushd %BUILD_DIR%
echo Using compiler....... %COMPILER%
echo Using linker......... %LINKER%
echo Using architecture... %Platform%
echo Using build type..... %BUILD_TYPE%
echo Using build root..... %CD%

View File

@ -1,13 +1,14 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
set COMPILERS=clang msvc
set BUILD_TYPES=debug relwithdebinfo
call %~dp0\init.cmd -c clang -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%b in (%BUILD_TYPES%) do (
call %~dp0\init.cmd -c %%c -g ninja -b %%b %*
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)

View File

@ -1,44 +1,24 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
set TEST_ARGS=%*
set BUILD_ROOT=%~dp0\..\build
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
call :execute_tests clang64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
set COMPILERS=clang msvc
set ARCHITECTURES=32 64
set BUILD_TYPES=debug release relwithdebinfo minsizerel
call :execute_tests clang32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%a in (%ARCHITECTURES%) do (
for %%b in (%BUILD_TYPES%) do (
call :execute_tests %%c%%a%%b
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)
)
goto :eof
@ -49,19 +29,27 @@ if not exist %BUILD_DIR% ( goto :eof )
pushd %BUILD_DIR%
echo Running tests from %CD%
call :execute_test app witest.app.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test cpplatest witest.cpplatest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test noexcept witest.noexcept.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test normal witest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
popd
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test sanitize-address witest.asan.exe
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test sanitize-undefined-behavior witest.ubsan.exe
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test win7 witest.win7.exe
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
goto :eof
:execute_tests_done
set EXIT_CODE=%ERRORLEVEL%
popd
exit /B %EXIT_CODE%
:execute_test
if not exist tests\%1\%2 ( goto :eof )
echo Running %1 tests...
tests\%1\%2
tests\%1\%2 %TEST_ARGS%
goto :eof

View File

@ -1,19 +1,98 @@
include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/common_build_flags.cmake)
# All projects need to reference the WIL headers
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/include)
# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that
include_directories(BEFORE SYSTEM ./workarounds/wrl)
# Because we don't always use msbuild, we need to run nuget manually
find_program(NUGET nuget)
if (NOT NUGET)
message(FATAL_ERROR "Unable to find the nuget CLI tool. Please install it from https://www.nuget.org/downloads and ensure it has been added to the PATH")
endif()
execute_process(COMMAND
${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE ret)
if (NOT ret EQUAL 0)
message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}")
endif()
set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe)
execute_process(COMMAND
${CPPWINRT} -input sdk -output include
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE ret)
if (NOT ret EQUAL 0)
message(FATAL_ERROR "Failed to run cppwinrt.exe")
endif()
include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include)
# The build pipelines have limitations that local development environments do not, so turn a few knobs
if (${FAST_BUILD})
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
endif()
# For some unknown reason, 'RelWithDebInfo' compiles with '/Ob1' as opposed to '/Ob2' which prevents inlining of
# functions not marked 'inline'. The reason we prefer 'RelWithDebInfo' over 'Release' is to get debug info, so manually
# revert to the desired (and default) inlining behavior as that exercises more interesting code paths
if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
# TODO: This is currently blocked by an apparent Clang bug: https://github.com/llvm/llvm-project/issues/59690
# replace_cxx_flag("/Ob1" "/Ob2")
endif()
set(COMMON_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/NTResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/wiTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../natvis/wil.natvis
)
add_subdirectory(app)
add_subdirectory(cpplatest)
add_subdirectory(noexcept)
add_subdirectory(normal)
add_subdirectory(win7)
set(DEBUG_BUILD FALSE)
set(HAS_DEBUG_INFO FALSE)
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
set(DEBUG_BUILD TRUE)
set(HAS_DEBUG_INFO TRUE)
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
set(HAS_DEBUG_INFO TRUE)
endif()
set(ASAN_AVAILABLE FALSE)
set(UBSAN_AVAILABLE FALSE)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# Address Sanitizer is available for all architectures and build types, but warns/errors if debug info is not enabled
set(ASAN_AVAILABLE ${HAS_DEBUG_INFO})
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Address Sanitizer is not available with debug libraries
set(ASAN_AVAILABLE NOT ${DEBUG_BUILD})
set(UBSAN_AVAILABLE NOT ${DEBUG_BUILD})
endif()
if (${ASAN_AVAILABLE})
add_subdirectory(sanitize-address)
endif()
if (${UBSAN_AVAILABLE})
add_subdirectory(sanitize-undefined-behavior)
endif()

View File

@ -0,0 +1,401 @@
#include <wil/com_apartment_variable.h>
#include <wil/com.h>
#include <functional>
#include "common.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
template <typename... args_t>
inline void LogOutput(_Printf_format_string_ PCWSTR format, args_t&&... args)
{
OutputDebugStringW(wil::str_printf_failfast<wil::unique_cotaskmem_string>(format, wistd::forward<args_t>(args)...).get());
}
inline bool IsComInitialized()
{
APTTYPE type{}; APTTYPEQUALIFIER qualifier{};
return CoGetApartmentType(&type, &qualifier) == S_OK;
}
inline void WaitForAllComApartmentsToRundown()
{
while (IsComInitialized())
{
Sleep(0);
}
}
void co_wait(const wil::unique_event& e)
{
HANDLE raw[] = { e.get() };
ULONG index{};
REQUIRE_SUCCEEDED(CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, INFINITE, static_cast<ULONG>(std::size(raw)), raw, &index));
}
void RunApartmentVariableTest(void(*test)())
{
test();
// Apartment variable rundown is async, wait for the last COM apartment
// to rundown before proceeding to the next test.
WaitForAllComApartmentsToRundown();
}
struct mock_platform
{
static unsigned long long GetApartmentId()
{
APTTYPE type; APTTYPEQUALIFIER qualifer;
REQUIRE_SUCCEEDED(CoGetApartmentType(&type, &qualifer)); // ensure COM is inited
// Approximate apartment Id
if (type == APTTYPE_STA)
{
REQUIRE_FALSE(GetCurrentThreadId() < APTTYPE_MAINSTA);
return GetCurrentThreadId();
}
else
{
// APTTYPE_MTA (1), APTTYPE_NA (2), APTTYPE_MAINSTA (3)
return type;
}
}
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
{
const auto id = GetApartmentId();
auto apt_observers = m_observers.find(id);
if (apt_observers == m_observers.end())
{
m_observers.insert({ id, { observer} });
}
else
{
apt_observers->second.emplace_back(observer);
}
return shutdown_type{ reinterpret_cast<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE>(id) };
}
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
{
auto id = reinterpret_cast<unsigned long long>(cookie);
m_observers.erase(id);
}
using shutdown_type = wil::unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&UnRegisterForApartmentShutdown), UnRegisterForApartmentShutdown>;
// This is needed to simulate the platform for unit testing.
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
{
return wil::scope_exit([aptId = GetCurrentThreadId(), init = wil::CoInitializeEx(coinitFlags)]()
{
const auto id = GetApartmentId();
auto apt_observers = m_observers.find(id);
if (apt_observers != m_observers.end())
{
const auto& observers = apt_observers->second;
for (auto& observer : observers)
{
observer->OnUninitialize(id);
}
m_observers.erase(apt_observers);
}
});
}
// Enable the test hook to force losing the race
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = 1; // enable test hook
inline static std::unordered_map<unsigned long long, std::vector<wil::com_ptr<IApartmentShutdown>>> m_observers;
};
auto fn() { return 42; };
auto fn2() { return 43; };
wil::apartment_variable<int, wil::apartment_variable_leak_action::ignore, mock_platform> g_v1;
wil::apartment_variable<int, wil::apartment_variable_leak_action::ignore> g_v2;
template <typename platform = wil::apartment_variable_platform>
void TestApartmentVariableAllMethods()
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
std::ignore = g_v1.get_or_create(fn);
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> v1;
REQUIRE(v1.get_if() == nullptr);
REQUIRE(v1.get_or_create(fn) == 42);
int value = 43;
v1.set(value);
REQUIRE(v1.get_or_create(fn) == 43);
REQUIRE(v1.get_existing() == 43);
v1.clear();
REQUIRE(v1.get_if() == nullptr);
}
template <typename platform = wil::apartment_variable_platform>
void TestApartmentVariableGetOrCreateForms()
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> v1;
REQUIRE(v1.get_or_create(fn) == 42);
v1.clear();
REQUIRE(v1.get_or_create([&]
{
return 1;
}) == 1);
v1.clear();
REQUIRE(v1.get_or_create() == 0);
}
template <typename platform = wil::apartment_variable_platform>
void TestApartmentVariableLifetimes()
{
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av1, av2;
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
auto v1 = av1.get_or_create(fn);
REQUIRE(av1.storage().size() == 1);
auto v2 = av1.get_existing();
REQUIRE(av1.current_apartment_variable_count() == 1);
REQUIRE(v1 == v2);
}
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
auto v1 = av1.get_or_create(fn);
auto v2 = av2.get_or_create(fn2);
REQUIRE((av1.current_apartment_variable_count() == 2));
REQUIRE(v1 != v2);
REQUIRE(av1.storage().size() == 1);
}
REQUIRE(av1.storage().size() == 0);
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
auto v = av1.get_or_create(fn);
REQUIRE(av1.current_apartment_variable_count() == 1);
std::thread([&]() // join below makes this ok
{
SetThreadDescription(GetCurrentThread(), L"STA");
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
std::ignore = av1.get_or_create(fn);
REQUIRE(av1.storage().size() == 2);
REQUIRE(av1.current_apartment_variable_count() == 1);
}).join();
REQUIRE(av1.storage().size() == 1);
av1.get_or_create(fn)++;
v = av1.get_existing();
REQUIRE(v == 43);
}
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
std::ignore = av1.get_or_create(fn);
REQUIRE(av1.current_apartment_variable_count() == 1);
int i = 1;
av1.set(i);
av1.clear();
REQUIRE(av1.current_apartment_variable_count() == 0);
// will fail fast since clear() was called.
// av1.set(1);
av1.clear_all_apartments_async().get();
}
REQUIRE(av1.storage().size() == 0);
}
template <typename platform = wil::apartment_variable_platform>
void TestMultipleApartments()
{
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av1, av2;
wil::unique_event t1Created{ wil::EventOptions::None }, t2Created{ wil::EventOptions::None };
wil::unique_event t1Shutdown{ wil::EventOptions::None }, t2Shutdown{ wil::EventOptions::None };
auto apt1_thread = std::thread([&]() // join below makes this ok
{
SetThreadDescription(GetCurrentThread(), L"STA 1");
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
std::ignore = av1.get_or_create(fn);
std::ignore = av2.get_or_create(fn);
t1Created.SetEvent();
co_wait(t1Shutdown);
});
auto apt2_thread = std::thread([&]() // join below makes this ok
{
SetThreadDescription(GetCurrentThread(), L"STA 2");
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
std::ignore = av1.get_or_create(fn);
std::ignore = av2.get_or_create(fn);
t2Created.SetEvent();
co_wait(t2Shutdown);
});
t1Created.wait();
t2Created.wait();
av1.clear_all_apartments_async().get();
av2.clear_all_apartments_async().get();
t1Shutdown.SetEvent();
t2Shutdown.SetEvent();
apt1_thread.join();
apt2_thread.join();
REQUIRE((wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform>::storage().size() == 0));
}
template <typename platform = wil::apartment_variable_platform>
void TestWinningApartmentAlreadyRundownRace()
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av;
std::ignore = av.get_or_create(fn);
const auto& storage = av.storage(); // for viewing the storage in the debugger
wil::unique_event otherAptVarCreated{ wil::EventOptions::None };
wil::unique_event startApartmentRundown{ wil::EventOptions::None };
wil::unique_event comRundownComplete{ wil::EventOptions::None };
auto apt_thread = std::thread([&]() // join below makes this ok
{
SetThreadDescription(GetCurrentThread(), L"STA");
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
std::ignore = av.get_or_create(fn);
otherAptVarCreated.SetEvent();
co_wait(startApartmentRundown);
});
otherAptVarCreated.wait();
// we now have av in this apartment and in the STA
REQUIRE(storage.size() == 2);
// wait for async clean to complete
av.clear_all_apartments_async().get();
startApartmentRundown.SetEvent();
REQUIRE(av.storage().size() == 0);
apt_thread.join();
}
template <typename platform = wil::apartment_variable_platform>
void TestLosingApartmentAlreadyRundownRace()
{
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av;
std::ignore = av.get_or_create(fn);
const auto& storage = av.storage(); // for viewing the storage in the debugger
wil::unique_event otherAptVarCreated{ wil::EventOptions::None };
wil::unique_event startApartmentRundown{ wil::EventOptions::None };
wil::unique_event comRundownComplete{ wil::EventOptions::None };
auto apt_thread = std::thread([&]() // join below makes this ok
{
SetThreadDescription(GetCurrentThread(), L"STA");
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
std::ignore = av.get_or_create(fn);
otherAptVarCreated.SetEvent();
co_wait(startApartmentRundown);
coUninit.reset();
comRundownComplete.SetEvent();
});
otherAptVarCreated.wait();
// we now have av in this apartment and in the STA
REQUIRE(storage.size() == 2);
auto clearAllOperation = av.clear_all_apartments_async();
startApartmentRundown.SetEvent();
comRundownComplete.wait();
clearAllOperation.get(); // wait for the async rundowns to complete
REQUIRE(av.storage().size() == 0);
apt_thread.join();
}
TEST_CASE("ComApartmentVariable::ShutdownRegistration", "[LocalOnly][com][unique_apartment_shutdown_registration]")
{
{
wil::unique_apartment_shutdown_registration r;
}
{
auto coUninit = wil::CoInitializeEx(COINIT_MULTITHREADED);
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
{
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
{
LogOutput(L"OnUninitialize %ull\n", apartmentId);
}
};
wil::unique_apartment_shutdown_registration apt_shutdown_registration;
unsigned long long id{};
REQUIRE_SUCCEEDED(::RoRegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get(), &id, apt_shutdown_registration.put()));
LogOutput(L"RoRegisterForApartmentShutdown %p\r\n", apt_shutdown_registration.get());
// don't unregister and let the pending COM apartment rundown invoke the callback.
apt_shutdown_registration.release();
}
}
TEST_CASE("ComApartmentVariable::CallAllMethods", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestApartmentVariableAllMethods<mock_platform>);
}
TEST_CASE("ComApartmentVariable::GetOrCreateForms", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms<mock_platform>);
}
TEST_CASE("ComApartmentVariable::VariableLifetimes", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestApartmentVariableLifetimes<mock_platform>);
}
TEST_CASE("ComApartmentVariable::WinningApartmentAlreadyRundownRace", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace<mock_platform>);
}
TEST_CASE("ComApartmentVariable::LosingApartmentAlreadyRundownRace", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace<mock_platform>);
}
TEST_CASE("ComApartmentVariable::MultipleApartments", "[com][apartment_variable]")
{
RunApartmentVariableTest(TestMultipleApartments<mock_platform>);
}
TEST_CASE("ComApartmentVariable::UseRealPlatformRunAllTests", "[com][apartment_variable]")
{
if (!wil::are_apartment_variables_supported())
{
return;
}
RunApartmentVariableTest(TestApartmentVariableAllMethods);
RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms);
RunApartmentVariableTest(TestApartmentVariableLifetimes);
RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace);
RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace);
RunApartmentVariableTest(TestMultipleApartments);
}
#endif

View File

@ -6,6 +6,8 @@
#include "common.h"
#include <Bits.h>
using namespace Microsoft::WRL;
// avoid including #include <shobjidl.h>, it fails to compile in noprivateapis
@ -130,6 +132,25 @@ TEST_CASE("ComTests::Test_Constructors", "[com][com_ptr]")
REQUIRE(ptrMove2.get() == &helper4);
REQUIRE(ptr2.get() == nullptr);
}
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201907L)
SECTION("CTAD pointer construction")
{
wil::com_ptr_nothrow ptr(&helper); // explicit
REQUIRE(IUnknownFake::GetAddRef() == 1);
REQUIRE(ptr.get() == &helper);
}
#endif
}
TEST_CASE("ComTests::Test_Make", "[com][com_ptr]")
{
IUnknownFake::Clear();
IUnknownFake helper;
auto ptr = wil::make_com_ptr_nothrow(&helper); // CTAD workaround for pre-C++20
REQUIRE(IUnknownFake::GetAddRef() == 1);
REQUIRE(ptr.get() == &helper);
}
TEST_CASE("ComTests::Test_Assign", "[com][com_ptr]")
@ -2231,6 +2252,90 @@ TEST_CASE("ComTests::VerifyCoGetClassObject", "[com][CoGetClassObject]")
}
#endif
#if defined(__IBackgroundCopyManager_INTERFACE_DEFINED__) && (__WI_LIBCPP_STD_VER >= 17)
TEST_CASE("ComTests::VerifyCoCreateEx", "[com][CoCreateInstance]")
{
auto init = wil::CoInitializeEx_failfast();
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto [sp1, ps1] = wil::CoCreateInstanceEx<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE((sp1 && ps1));
#endif
auto [hr, unk] = wil::CoCreateInstanceExNoThrow<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE_SUCCEEDED(hr);
auto sp = std::get<0>(unk);
auto ps = std::get<1>(unk);
REQUIRE((sp && ps));
auto [sp3, ps3] = wil::CoCreateInstanceExFailFast<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE((sp3 && ps3));
}
#ifdef WIL_ENABLE_EXCEPTIONS
{
auto [ps, pf] = wil::CoCreateInstanceEx<IPersistStream, IPersistFile>(__uuidof(ShellLink), CLSCTX_INPROC_SERVER);
std::ignore = ps->IsDirty();
std::ignore = pf->IsDirty();
}
#endif
}
TEST_CASE("ComTests::VerifyCoCreateInstanceExNoThrowMissingInterface", "[com][CoCreateInstance]")
{
auto init = wil::CoInitializeEx_failfast();
{
// IPropertyBag is not implemented
auto [error, result] = wil::CoCreateInstanceExNoThrow<IBackgroundCopyManager, IUnknown, IPropertyBag>
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE(error == E_NOINTERFACE);
REQUIRE(std::get<0>(result).get() == nullptr);
REQUIRE(std::get<1>(result).get() == nullptr);
REQUIRE(std::get<2>(result).get() == nullptr);
}
}
TEST_CASE("ComTests::VerifyTryCoCreateInstanceMissingInterface", "[com][CoCreateInstance]")
{
auto init = wil::CoInitializeEx_failfast();
// request some implemented, one not (IPropertyBag), partial results enabled
{
auto [sp, pb] = wil::TryCoCreateInstanceEx<IBackgroundCopyManager, IPropertyBag>
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE(sp != nullptr);
REQUIRE(pb == nullptr);
}
{
auto [sp, pb] = wil::TryCoCreateInstanceExNoThrow<IBackgroundCopyManager, IPropertyBag>
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE(sp != nullptr);
REQUIRE(pb == nullptr);
}
{
auto [sp, pb] = wil::TryCoCreateInstanceExFailFast<IBackgroundCopyManager, IPropertyBag>
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
REQUIRE(sp != nullptr);
REQUIRE(pb == nullptr);
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("ComTests::VerifyQueryMultipleInterfaces", "[com][com_multi_query]")
{
auto init = wil::CoInitializeEx_failfast();
auto mgr = wil::CoCreateInstance<BackgroundCopyManager>(CLSCTX_LOCAL_SERVER);
auto [sp, ps] = wil::com_multi_query<IBackgroundCopyManager, IUnknown>(mgr.get());
REQUIRE(sp);
REQUIRE(ps);
auto [sp1, pb] = wil::try_com_multi_query<IBackgroundCopyManager, IPropertyBag>(mgr.get());
REQUIRE(sp1);
REQUIRE(!pb);
}
#endif
#endif // __IBackgroundCopyManager_INTERFACE_DEFINED__
#ifdef __IObjectWithSite_INTERFACE_DEFINED__
TEST_CASE("ComTests::VerifyComSetSiteNullIsMoveOnly", "[com][com_set_site]")
{

View File

@ -159,7 +159,7 @@ enum class EClassTest
};
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
enum ERawTest
enum ERawTest : unsigned int
{
ER_None = 0x0,
ER_One = 0x1,

View File

@ -3,6 +3,8 @@
// However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler'
// global function pointer should be set, so these should all run successfully
#include <inspectable.h> // Must be included before base.h
#include <winrt/base.h>
#include <wil/result.h>

View File

@ -1,5 +1,10 @@
#include <wil/cppwinrt.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/cppwinrt_helpers.h>
#include <winrt/Windows.System.h>
#include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
#include "catch.hpp"
@ -22,6 +27,145 @@ static const HRESULT cppwinrt_mapped_hresults[] =
E_OUTOFMEMORY,
};
template<typename T> auto copy_thing(T const& src)
{
return std::decay_t<T>(src);
}
template<typename T, typename K>
void CheckMapVector(std::vector<winrt::Windows::Foundation::Collections::IKeyValuePair<T, K>> const& test, std::map<T, K> const& src)
{
REQUIRE(test.size() == src.size());
for (auto&& i : test)
{
REQUIRE(i.Value() == src.at(i.Key()));
}
}
struct vector_like
{
uint32_t Size() const { return 100; }
int GetAt(uint32_t) const { return 15; }
uint32_t GetMany(uint32_t start, winrt::array_view<int> items) const
{
if (start > 0)
{
throw winrt::hresult_out_of_bounds();
}
uint32_t const to_fill = (std::min)(items.size(), Size());
std::fill_n(items.begin(), to_fill, GetAt(0));
return to_fill;
}
};
struct iterator_like
{
static const uint32_t total = 20;
mutable uint32_t remaining = total;
int Current() const { return 3; }
uint32_t GetMany(winrt::array_view<int> items) const
{
auto to_copy = (std::min)(items.size(), remaining);
std::fill_n(items.begin(), to_copy, Current());
remaining -= to_copy;
return to_copy;
}
};
struct iterable_like
{
auto First() const { return iterator_like{}; }
};
struct unstable_vector : winrt::implements<unstable_vector, winrt::Windows::Foundation::Collections::IVectorView<int>>
{
auto Size() { return 4; }
int GetAt(uint32_t) { return 7; }
uint32_t GetMany(uint32_t, winrt::array_view<int> items)
{
std::fill(items.begin(), items.end(), GetAt(0));
return items.size();
}
bool IndexOf(int, uint32_t) { throw winrt::hresult_not_implemented(); }
};
TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
{
winrt::init_apartment();
{
std::vector<winrt::hstring> src_vector = { L"foo", L"bar", L"bas" };
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
REQUIRE(wil::to_vector(sv) == src_vector);
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
REQUIRE(wil::to_vector(sv.First()) == src_vector);
REQUIRE(wil::to_vector(sv.First()) == src_vector);
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) == src_vector);
}
{
std::vector<uint32_t> src_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
REQUIRE(wil::to_vector(sv) == src_vector);
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
REQUIRE(wil::to_vector(sv.First()) == src_vector);
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<uint32_t>>()) == src_vector);
}
{
std::vector<float> src_vector;
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
REQUIRE(wil::to_vector(sv) == src_vector);
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
REQUIRE(wil::to_vector(sv.First()) == src_vector);
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<float>>()) == src_vector);
}
{
std::map<winrt::hstring, winrt::hstring> src_map{{L"kittens", L"fluffy"}, {L"puppies", L"cute"}};
auto sm = winrt::single_threaded_map(copy_thing(src_map));
CheckMapVector(wil::to_vector(sm), src_map);
CheckMapVector(wil::to_vector(sm.GetView()), src_map);
CheckMapVector(wil::to_vector(sm.First()), src_map);
}
{
winrt::Windows::Foundation::Collections::PropertySet props;
props.Insert(L"kitten", winrt::box_value(L"fluffy"));
props.Insert(L"puppy", winrt::box_value<uint32_t>(25));
auto converted = wil::to_vector(props);
REQUIRE(converted.size() == props.Size());
for (auto&& kv : converted)
{
if (kv.Key() == L"kitten")
{
REQUIRE(kv.Value().as<winrt::hstring>() == L"fluffy");
}
else if (kv.Key() == L"puppy")
{
REQUIRE(kv.Value().as<uint32_t>() == 25);
}
else
{
REQUIRE(false);
}
}
}
REQUIRE_THROWS(wil::to_vector(winrt::make<unstable_vector>()));
auto ilike = wil::to_vector(iterable_like{});
REQUIRE(ilike.size() == iterator_like::total);
for (auto&& i : ilike) REQUIRE(i == iterator_like{}.Current());
auto vlike = wil::to_vector(vector_like{});
REQUIRE(vlike.size() == vector_like{}.Size());
for (auto&& i : vlike) REQUIRE(i == vector_like{}.GetAt(0));
winrt::clear_factory_cache();
winrt::uninit_apartment();
}
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
@ -142,3 +286,159 @@ TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
// NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
// that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
}
TEST_CASE("CppWinRTTests::ModuleReference", "[cppwinrt]")
{
auto peek_module_ref_count = []()
{
++winrt::get_module_lock();
return --winrt::get_module_lock();
};
auto initial = peek_module_ref_count();
// Basic test: Construct and destruct.
{
auto module_ref = wil::winrt_module_reference();
REQUIRE(peek_module_ref_count() == initial + 1);
}
REQUIRE(peek_module_ref_count() == initial);
// Fancy test: Copy object with embedded reference.
{
struct object_with_ref
{
wil::winrt_module_reference ref;
};
object_with_ref o1;
REQUIRE(peek_module_ref_count() == initial + 1);
auto o2 = o1;
REQUIRE(peek_module_ref_count() == initial + 2);
o1 = o2;
REQUIRE(peek_module_ref_count() == initial + 2);
o2 = std::move(o1);
REQUIRE(peek_module_ref_count() == initial + 2);
}
REQUIRE(peek_module_ref_count() == initial);
}
#if (!defined(__clang__) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
// Define our own custom dispatcher that we can force it to behave in certain ways.
// wil::resume_foreground supports any dispatcher that has a dispatcher_traits.
namespace test
{
enum class TestDispatcherPriority
{
Normal = 0,
Weird = 1,
};
using TestDispatcherHandler = winrt::delegate<>;
enum class TestDispatcherMode
{
Dispatch,
RaceDispatch,
Orphan,
Fail,
};
struct TestDispatcher
{
TestDispatcher() = default;
TestDispatcher(TestDispatcher const&) = delete;
TestDispatcherMode mode = TestDispatcherMode::Dispatch;
TestDispatcherPriority expected_priority = TestDispatcherPriority::Normal;
void TryEnqueue(TestDispatcherPriority priority, TestDispatcherHandler const& handler) const
{
REQUIRE(priority == expected_priority);
if (mode == TestDispatcherMode::Fail)
{
throw winrt::hresult_not_implemented();
}
if (mode == TestDispatcherMode::RaceDispatch)
{
handler();
return;
}
std::ignore = [](auto mode, auto handler) ->winrt::fire_and_forget
{
co_await winrt::resume_background();
if (mode == TestDispatcherMode::Dispatch)
{
handler();
}
}(mode, handler);
}
};
}
namespace wil::details
{
template<>
struct dispatcher_traits<test::TestDispatcher>
{
using Priority = test::TestDispatcherPriority;
using Handler = test::TestDispatcherHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
TEST_CASE("CppWinRTTests::ResumeForegroundTests", "[cppwinrt]")
{
// Verify that the DispatcherQueue version has been unlocked.
using Verify = decltype(wil::resume_foreground(winrt::Windows::System::DispatcherQueue{ nullptr }));
static_assert(wistd::is_trivial_v<Verify> || !wistd::is_trivial_v<Verify>);
[]() -> winrt::Windows::Foundation::IAsyncAction
{
test::TestDispatcher dispatcher;
// Normal case: Resumes on new thread.
dispatcher.mode = test::TestDispatcherMode::Dispatch;
co_await wil::resume_foreground(dispatcher);
// Race case: Resumes before TryEnqueue returns.
dispatcher.mode = test::TestDispatcherMode::RaceDispatch;
co_await wil::resume_foreground(dispatcher);
// Orphan case: Never resumes, detected when handler is destructed without ever being invoked.
dispatcher.mode = test::TestDispatcherMode::Orphan;
bool seen = false;
try
{
co_await wil::resume_foreground(dispatcher);
}
catch (winrt::hresult_error const& e)
{
seen = e.code() == HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE));
}
REQUIRE(seen);
// Fail case: Can't even schedule the resumption.
dispatcher.mode = test::TestDispatcherMode::Fail;
seen = false;
try
{
co_await wil::resume_foreground(dispatcher);
}
catch (winrt::hresult_not_implemented const&)
{
seen = true;
}
REQUIRE(seen);
// Custom priority.
dispatcher.mode = test::TestDispatcherMode::Dispatch;
dispatcher.expected_priority = test::TestDispatcherPriority::Weird;
co_await wil::resume_foreground(dispatcher, test::TestDispatcherPriority::Weird);
}().get();
}
#endif // coroutines

View File

@ -42,7 +42,7 @@ struct WinRTStorage<HSTRING>
{
Microsoft::WRL::Wrappers::HString value;
HRESULT CopyTo(HSTRING* result)
HRESULT CopyTo(HSTRING* result) const
{
return value.CopyTo(result);
}
@ -63,7 +63,7 @@ struct WinRTStorage<HSTRING>
value = {};
}
bool Equals(HSTRING val)
bool Equals(HSTRING val) const
{
return value == val;
}
@ -207,7 +207,7 @@ private:
WinRTStorage<Abi> m_storage;
};
template <typename Logical, typename Abi = Logical, size_t MaxSize = 42>
template <typename Logical, typename Abi = Logical, size_t MaxSize = 250>
struct FakeVector : Microsoft::WRL::RuntimeClass<
ABI::Windows::Foundation::Collections::IVector<Logical>,
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
@ -292,7 +292,7 @@ struct FakeVector : Microsoft::WRL::RuntimeClass<
for (size_t i = index + 1; i < m_size; ++i)
{
wistd::swap_wil(m_data[i - 1], m_data[i]);
wistd::swap_wil(m_data[i], m_data[i - 1]);
}
m_data[--m_size].Reset();
@ -349,8 +349,8 @@ struct FakeVector : Microsoft::WRL::RuntimeClass<
count = (count > capacity) ? capacity : count;
HRESULT hr = S_OK;
unsigned i = 0;
for (; (i < count) && SUCCEEDED(hr); ++i)
unsigned i;
for (i = 0; (i < count) && SUCCEEDED(hr); ++i)
{
hr = m_data[startIndex + i].CopyTo(value + i);
}

View File

@ -34,6 +34,13 @@ bool DirectoryExists(_In_ PCWSTR path)
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
bool FileExists(_In_ PCWSTR path)
{
DWORD dwAttrib = GetFileAttributesW(path);
return (dwAttrib != INVALID_FILE_ATTRIBUTES);
}
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
{
wchar_t basePath[MAX_PATH];
@ -80,6 +87,99 @@ TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
}
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveDoesNotTraverseWithoutAHandle", "[filesystem]")
{
auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
return path;
};
wil::unique_cotaskmem_string tempPath;
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
auto scopeGuard = wil::scope_exit([&]
{
wil::RemoveDirectoryRecursiveNoThrow(basePath.get());
});
// Try to delete a directory whose handle is already taken.
const auto folderToRecurse = CreateRelativePath(basePath.get(), L"folderToRecurse");
REQUIRE(::CreateDirectoryW(folderToRecurse.get(), nullptr));
const auto subfolderWithHandle = CreateRelativePath(folderToRecurse.get(), L"subfolderWithHandle");
REQUIRE(::CreateDirectoryW(subfolderWithHandle.get(), nullptr));
const auto childOfSubfolder = CreateRelativePath(subfolderWithHandle.get(), L"childOfSubfolder");
REQUIRE(::CreateDirectoryW(childOfSubfolder.get(), nullptr));
// Passing a 0 in share flags only allows metadata query on this file by other processes.
// This should fail with a sharing violation error when any other action is taken.
wil::unique_hfile subFolderHandle(::CreateFileW(subfolderWithHandle.get(), GENERIC_ALL,
0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
REQUIRE(subFolderHandle);
REQUIRE(wil::RemoveDirectoryRecursiveNoThrow(folderToRecurse.get()) == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION));
// Release the handle to allow cleanup.
subFolderHandle.reset();
}
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteReadOnlyFiles", "[filesystem]")
{
auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
return path;
};
auto CreateReadOnlyFile = [](PCWSTR path)
{
wil::unique_hfile fileHandle(CreateFileW(path, 0,
0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_READONLY, nullptr));
REQUIRE(fileHandle);
};
wil::unique_cotaskmem_string tempPath;
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
auto scopeGuard = wil::scope_exit([&]
{
wil::RemoveDirectoryRecursiveNoThrow(basePath.get(), wil::RemoveDirectoryOptions::RemoveReadOnly);
});
// Create a reparse point and a target folder that shouldn't get deleted
auto folderToDelete = CreateRelativePath(basePath.get(), L"folderToDelete");
REQUIRE(::CreateDirectoryW(folderToDelete.get(), nullptr));
auto topLevelReadOnly = CreateRelativePath(folderToDelete.get(), L"topLevelReadOnly.txt");
CreateReadOnlyFile(topLevelReadOnly.get());
auto subLevel = CreateRelativePath(folderToDelete.get(), L"subLevel");
REQUIRE(::CreateDirectoryW(subLevel.get(), nullptr));
auto subLevelReadOnly = CreateRelativePath(subLevel.get(), L"subLevelReadOnly.txt");
CreateReadOnlyFile(subLevelReadOnly.get());
// Delete will fail without the RemoveReadOnlyFlag
REQUIRE_FAILED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get()));
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get(), wil::RemoveDirectoryOptions::RemoveReadOnly));
// Verify all files have been deleted
REQUIRE_FALSE(FileExists(subLevelReadOnly.get()));
REQUIRE_FALSE(DirectoryExists(subLevel.get()));
REQUIRE_FALSE(FileExists(topLevelReadOnly.get()));
REQUIRE_FALSE(DirectoryExists(folderToDelete.get()));
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
@ -440,8 +540,28 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
#ifdef WIL_ENABLE_EXCEPTIONS
auto wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
#endif
}
#ifdef WIL_ENABLE_EXCEPTIONS
wil::unique_cotaskmem_string NativeGetModuleFileNameWrap(HANDLE processHandle, HMODULE moduleHandle)
{
DWORD size = MAX_PATH * 4;
auto path = wil::make_cotaskmem_string_nothrow(nullptr, size);
DWORD copied = processHandle ?
::GetModuleFileNameExW(processHandle, moduleHandle, path.get(), size) :
::GetModuleFileNameW(moduleHandle, path.get(), size);
REQUIRE(copied < size);
return path;
}
#endif
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
{
wil::unique_cotaskmem_string path;
@ -455,6 +575,58 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
#ifdef WIL_ENABLE_EXCEPTIONS
auto wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, nullptr);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), nullptr);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), nullptr).get());
wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, kernel32);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), kernel32);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), kernel32).get());
wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(kernel32);
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
#endif
}
TEST_CASE("FileSystemTests::QueryFullProcessImageNameW and GetModuleFileNameW", "[filesystem]")
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto procName = wil::QueryFullProcessImageNameW<std::wstring>();
auto moduleName = wil::GetModuleFileNameW<std::wstring>();
REQUIRE(procName == moduleName);
#endif
}
TEST_CASE("FileSystemTests::QueryFullProcessImageNameW", "[filesystem]")
{
WCHAR fullName[MAX_PATH * 4];
DWORD fullNameSize = ARRAYSIZE(fullName);
REQUIRE(::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, fullName, &fullNameSize));
wil::unique_cotaskmem_string path;
REQUIRE_SUCCEEDED(wil::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, path));
REQUIRE(wcscmp(fullName, path.get()) == 0);
wil::unique_cotaskmem nativePath;
REQUIRE_SUCCEEDED((wil::QueryFullProcessImageNameW<wil::unique_cotaskmem_string, 15>(::GetCurrentProcess(), 0, path)));
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

165
Externals/WIL/tests/NtResultTests.cpp vendored Normal file
View File

@ -0,0 +1,165 @@
#include <wil/result.h>
#include <wil/nt_result_macros.h>
#include "common.h"
#define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL)
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
#define STATUS_INVALID_CONNECTION ((NTSTATUS)0xC0000140L)
#define E_LOAD_NAMESERVICE_FAILED ((HRESULT)0x80000140L)
TEST_CASE("NtResultTests::NtReturn", "[result]")
{
auto status = []()
{
NT_RETURN_NTSTATUS(STATUS_INVALID_CONNECTION);
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG");
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter");
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
NT_RETURN_IF_NTSTATUS_FAILED(STATUS_INVALID_CONNECTION);
return STATUS_SUCCESS;
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
NT_RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter");
return STATUS_SUCCESS;
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
NT_RETURN_IF_NTSTATUS_FAILED(STATUS_SUCCESS);
return STATUS_INVALID_CONNECTION;
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("NtResultTests::NtThrowCatch", "[result]")
{
// Throw NTSTATUS with immediate conversion to HRESULT. HRESULT would appear in the logs.
auto hr = []()
{
try
{
THROW_NTSTATUS(STATUS_INVALID_CONNECTION);
}
CATCH_RETURN();
}();
// THROW_NTSTATUS converts NTSTATUS to HRESULT through WIN32 error code.
REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
// Verify that conversion NTSTATUS -> HRESULT -> NTSTATUS is not 1:1.
auto status = []()
{
try
{
THROW_HR(wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
}
NT_CATCH_RETURN();
}();
if (wil::details::g_pfnRtlNtStatusToDosErrorNoTeb)
{
REQUIRE(status != STATUS_INVALID_CONNECTION);
}
else
{
REQUIRE(status == STATUS_INVALID_CONNECTION);
}
// Throw HRESULT with conversion to NTSTATUS on a best effort. NTSTATUS would appear in the logs.
status = []()
{
try
{
THROW_HR(__HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
}
NT_CATCH_RETURN();
}();
REQUIRE(status == STATUS_OBJECT_PATH_NOT_FOUND);
// Throw HRESULT with conversion to NTSTATUS on a best effort that maps to generic error. NTSTATUS would appear in the logs.
status = []()
{
try
{
THROW_HR(E_LOAD_NAMESERVICE_FAILED);
}
NT_CATCH_RETURN();
}();
REQUIRE(status == STATUS_INTERNAL_ERROR);
// Throw NTSTATUS without conversion. NTSTATUS would appear in the logs.
status = []()
{
try
{
THROW_NTSTATUS(STATUS_INVALID_CONNECTION);
}
NT_CATCH_RETURN();
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
try
{
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
}
NT_CATCH_RETURN();
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
status = []()
{
try
{
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS with custom catch");
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return wil::StatusFromCaughtException();
}
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
hr = []()
{
try
{
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
}
CATCH_RETURN();
}();
REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
status = []()
{
try
{
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
}
NT_CATCH_RETURN_MSG("Catching STATUS_INVALID_CONNECTION thrown by NT_THROW_NTSTATUS_MSG");
}();
REQUIRE(status == STATUS_INVALID_CONNECTION);
}
#endif

View File

@ -9,6 +9,7 @@
#include <memory>
#include <roapi.h>
#include <winstring.h>
#include <WinUser.h>
#include <wil/resource.h>
#include <wrl/implements.h>
@ -59,6 +60,14 @@ TEST_CASE("ResourceTests::TestLastErrorContext", "[resource][last_error_context]
SetLastError(1);
}
REQUIRE(GetLastError() == 1);
// The value in the context is unimpacted by other things changing the last error
{
SetLastError(42);
auto error42 = wil::last_error_context();
SetLastError(1);
REQUIRE(error42.value() == 42);
}
}
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
@ -267,6 +276,40 @@ void UniqueProcessInfo()
}
#endif
// Compilation only test...
#ifdef WIL_ENABLE_EXCEPTIONS
void NoexceptConstructibleTest()
{
using BaseStorage = wil::details::unique_storage<wil::details::handle_resource_policy>;
struct ThrowingConstructor : BaseStorage
{
ThrowingConstructor() = default;
explicit ThrowingConstructor(HANDLE) __WI_NOEXCEPT_(false) {}
};
struct ProtectedConstructor : BaseStorage
{
protected:
ProtectedConstructor() = default;
explicit ProtectedConstructor(HANDLE) WI_NOEXCEPT {}
};
// wil::unique_handle is one of the many types which are expected to be noexcept
// constructible since they don't perform any "advanced" initialization.
static_assert(wistd::is_nothrow_default_constructible_v<wil::unique_handle>, "wil::unique_any_t should always be nothrow default constructible");
static_assert(wistd::is_nothrow_constructible_v<wil::unique_handle, HANDLE>, "wil::unique_any_t should be noexcept if the storage is");
// The inverse: A throwing storage constructor.
static_assert(wistd::is_nothrow_default_constructible_v<wil::unique_any_t<ThrowingConstructor>>, "wil::unique_any_t should always be nothrow default constructible");
static_assert(!wistd::is_nothrow_constructible_v<wil::unique_any_t<ThrowingConstructor>, HANDLE>, "wil::unique_any_t shouldn't be noexcept if the storage isn't");
// With a protected constructor wil::unique_any_t will be unable to correctly
// "forward" the noexcept attribute, but the code should still compile.
wil::unique_any_t<ProtectedConstructor> p{ INVALID_HANDLE_VALUE };
}
#endif
struct FakeComInterface
{
void AddRef()
@ -732,4 +775,90 @@ TEST_CASE("DefaultTemplateParamCompiles", "[resource]")
wil::unique_midl_ptr<> g;
wil::unique_cotaskmem_ptr<> h;
wil::unique_mapview_ptr<> i;
}
TEST_CASE("UniqueInvokeCleanupMembers", "[resource]")
{
// Case 1 - unique_ptr<> for a T* that has a "destroy" member
struct ThingWithDestroy
{
bool destroyed = false;
void destroy() { destroyed = true; };
};
ThingWithDestroy toDestroy;
wil::unique_any<ThingWithDestroy*, decltype(&ThingWithDestroy::destroy), &ThingWithDestroy::destroy> p(&toDestroy);
p.reset();
REQUIRE(!p);
REQUIRE(toDestroy.destroyed);
// Case 2 - unique_struct calling a member, like above
struct ThingToDestroy2
{
bool* destroyed;
void destroy() { *destroyed = true; };
};
bool structDestroyed = false;
{
wil::unique_struct<ThingToDestroy2, decltype(&ThingToDestroy2::destroy), &ThingToDestroy2::destroy> other;
other.destroyed = &structDestroyed;
REQUIRE(!structDestroyed);
}
REQUIRE(structDestroyed);
}
struct ITokenTester : IUnknown
{
virtual void DirectClose(DWORD_PTR token) = 0;
};
struct TokenTester : ITokenTester
{
IFACEMETHOD_(ULONG, AddRef)() override { return 2; }
IFACEMETHOD_(ULONG, Release)() override { return 1; }
IFACEMETHOD(QueryInterface)(REFIID, void**) { return E_NOINTERFACE; }
void DirectClose(DWORD_PTR token) override {
m_closed = (token == m_closeToken);
}
bool m_closed = false;
DWORD_PTR m_closeToken;
};
void MyTokenTesterCloser(ITokenTester* tt, DWORD_PTR token)
{
tt->DirectClose(token);
}
TEST_CASE("ComTokenCloser", "[resource]")
{
using token_tester_t = wil::unique_com_token<ITokenTester, DWORD_PTR, decltype(MyTokenTesterCloser), &MyTokenTesterCloser>;
TokenTester tt;
tt.m_closeToken = 4;
{
token_tester_t tmp{ &tt, 4 };
}
REQUIRE(tt.m_closed);
}
TEST_CASE("ComTokenDirectCloser", "[resource]")
{
using token_tester_t = wil::unique_com_token<ITokenTester, DWORD_PTR, decltype(&ITokenTester::DirectClose), &ITokenTester::DirectClose>;
TokenTester tt;
tt.m_closeToken = 4;
{
token_tester_t tmp{ &tt, 4 };
}
REQUIRE(tt.m_closed);
}
TEST_CASE("UniqueCloseClipboardCall", "[resource]")
{
#if defined(__WIL__WINUSER_) && !defined(NOCLIPBOARD)
if (auto clip = wil::open_clipboard(nullptr))
{
REQUIRE(::EmptyClipboard());
}
#endif
}

View File

@ -1,7 +1,10 @@
#include <wil/com.h>
#include <wil/result.h>
#if (NTDDI_VERSION >= NTDDI_WIN8)
#include <wil/result_originate.h>
#endif
#include <roerrorapi.h>
@ -323,7 +326,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]")
RETURN_CAUGHT_EXCEPTION_EXPECTED();
}
}();
REQUIRE(failures.size() == 0);
REQUIRE(failures.empty());
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
@ -339,7 +342,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]")
{
throw std::bad_alloc();
});
REQUIRE(failures.size() == 0);
REQUIRE(failures.empty());
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
@ -436,16 +439,16 @@ void ExceptionHandlingCompilationTest()
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
{
THROW_HR(E_FAIL);
});
hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
{
});
hr = wil::ResultFromException([&]
wil::ResultFromException([&]
{
});
@ -479,7 +482,7 @@ TEST_CASE("ResultTests::ErrorMacros", "[result]")
}
// The originate helper isn't compatible with CX so don't test it in that mode.
#ifndef __cplusplus_winrt
#if !defined(__cplusplus_winrt) && (NTDDI_VERSION >= NTDDI_WIN8)
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
{
::wil::SetOriginateErrorCallback(nullptr);
@ -572,4 +575,17 @@ TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
}
#endif // __cplusplus_winrt
#endif
TEST_CASE("ResultTests::ReportDoesNotChangeLastError", "[result]")
{
decltype(wil::details::g_pfnLoggingCallback) oopsie = [](wil::FailureInfo const&) noexcept
{
::SetLastError(ERROR_ABANDON_HIBERFILE);
};
auto swap = witest::AssignTemporaryValue(&wil::details::g_pfnLoggingCallback, oopsie);
::SetLastError(ERROR_ABIOS_ERROR);
LOG_IF_WIN32_BOOL_FALSE(FALSE);
REQUIRE(::GetLastError() == ERROR_ABIOS_ERROR);
}

View File

@ -11,8 +11,8 @@ void RpcMethodReturnsVoid(ULONG toRaise)
}
struct FOO_CONTEXT_T {};
typedef FOO_CONTEXT_T* FOO_CONTEXT;
typedef FOO_CONTEXT* PFOO_CONTEXT;
using FOO_CONTEXT = FOO_CONTEXT_T*;
using PFOO_CONTEXT = FOO_CONTEXT*;
void CloseContextHandle(_Inout_ PFOO_CONTEXT)
{

View File

@ -45,3 +45,140 @@ TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
}
}
#if __WI_LIBCPP_STD_VER >= 17
struct CustomNoncopyableString
{
CustomNoncopyableString() = default;
CustomNoncopyableString(const CustomNoncopyableString&) = delete;
void operator=(const CustomNoncopyableString&) = delete;
constexpr operator PCSTR() const { return "hello"; }
constexpr operator PCWSTR() const { return L"w-hello"; }
};
TEST_CASE("StlTests::TestZStringView", "[stl][zstring_view]")
{
// Test empty cases
REQUIRE(wil::zstring_view{}.length() == (size_t)0u);
REQUIRE(wil::zstring_view{}.data() == nullptr);
REQUIRE(wil::zstring_view{}.c_str() == nullptr);
// Test empty string cases
REQUIRE(wil::zstring_view{""}[0] == '\0');
REQUIRE(wil::zstring_view{""}.c_str()[0] == '\0');
REQUIRE(wil::zstring_view{""}.length() == 0);
// Test different constructor equality
constexpr wil::zstring_view fromLiteral = "abc";
REQUIRE(fromLiteral.length() == strlen("abc"));
std::string stlString = "abc";
wil::zstring_view fromString(stlString);
wil::zstring_view fromPtr(stlString.data());
static constexpr char charArray[] = "abc";
constexpr wil::zstring_view fromArray(charArray);
static constexpr char extendedCharArray[] = "abc\0\0\0\0\0";
constexpr wil::zstring_view fromExtendedArray(extendedCharArray);
wil::zstring_view copy = fromLiteral;
REQUIRE(fromLiteral == stlString);
REQUIRE(fromLiteral == fromString);
REQUIRE(fromLiteral == fromArray);
REQUIRE(fromLiteral == fromExtendedArray);
REQUIRE(fromLiteral == copy);
// Test decay to std::string_view
std::string_view sv = fromLiteral;
REQUIRE(sv == fromLiteral);
// Test operator[]
REQUIRE(fromLiteral[0] == 'a');
REQUIRE(fromLiteral[1] == 'b');
REQUIRE(fromLiteral[2] == 'c');
REQUIRE(fromLiteral[3] == '\0');
// Test constructing with no NULL in range
static constexpr char badCharArray[2][3] = {{'a', 'b', 'c' }, {'a', 'b', 'c' }};
REQUIRE_CRASH((wil::zstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) }));
REQUIRE_CRASH((wil::zstring_view{ badCharArray[0] }));
// Test constructing with a NULL one character past the valid range, guarding against off-by-one errors
// Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer
static constexpr char badCharArrayOffByOne[2][3] = {{'a', 'b', 'c' }, {}};
const wil::zstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0]));
REQUIRE(fromLiteral == fromTerminatedCharArray);
REQUIRE_CRASH((wil::zstring_view{ badCharArrayOffByOne[0] }));
// Test constructing from custom string type
CustomNoncopyableString customString;
wil::zstring_view fromCustomString(customString);
REQUIRE(fromCustomString == (PCSTR)customString);
}
TEST_CASE("StlTests::TestZWStringView", "[stl][zstring_view]")
{
// Test empty cases
REQUIRE(wil::zwstring_view{}.length() == (size_t)0u);
REQUIRE(wil::zwstring_view{}.data() == nullptr);
REQUIRE(wil::zwstring_view{}.c_str() == nullptr);
// Test empty string cases
REQUIRE(wil::zwstring_view{L""}[0] == L'\0');
REQUIRE(wil::zwstring_view{L""}.c_str()[0] == L'\0');
REQUIRE(wil::zwstring_view{L""}.length() == 0);
// Test different constructor equality
constexpr wil::zwstring_view fromLiteral = L"abc";
REQUIRE(fromLiteral.length() == wcslen(L"abc"));
std::wstring stlString = L"abc";
wil::zwstring_view fromString(stlString);
wil::zwstring_view fromPtr(stlString.data());
static constexpr wchar_t charArray[] = L"abc";
constexpr wil::zwstring_view fromArray(charArray);
static constexpr wchar_t extendedCharArray[] = L"abc\0\0\0\0\0";
constexpr wil::zwstring_view fromExtendedArray(extendedCharArray);
wil::zwstring_view copy = fromLiteral;
REQUIRE(fromLiteral == stlString);
REQUIRE(fromLiteral == fromString);
REQUIRE(fromLiteral == fromArray);
REQUIRE(fromLiteral == fromExtendedArray);
REQUIRE(fromLiteral == copy);
// Test decay to std::wstring_view
std::wstring_view sv = fromLiteral;
REQUIRE(sv == fromLiteral);
// Test operator[]
REQUIRE(fromLiteral[0] == L'a');
REQUIRE(fromLiteral[1] == L'b');
REQUIRE(fromLiteral[2] == L'c');
REQUIRE(fromLiteral[3] == L'\0');
// Test constructing with no NULL in range
static constexpr wchar_t badCharArray[2][3] = {{ L'a', L'b', L'c' }, { L'a', L'b', L'c' } };
REQUIRE_CRASH((wil::zwstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) }));
REQUIRE_CRASH((wil::zwstring_view{ badCharArray[0] }));
// Test constructing with a NULL one character past the valid range, guarding against off-by-one errors
// Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer
static constexpr wchar_t badCharArrayOffByOne[2][3] = {{ L'a', L'b', L'c' }, {}};
const wil::zwstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0]));
REQUIRE(fromLiteral == fromTerminatedCharArray);
REQUIRE_CRASH((wil::zwstring_view{ badCharArrayOffByOne[0] }));
// Test constructing from custom string type
CustomNoncopyableString customString;
wil::zwstring_view fromCustomString(customString);
REQUIRE(fromCustomString == (PCWSTR)customString);
}
#endif

View File

@ -38,6 +38,7 @@ TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
}
#endif
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
{
SECTION("Passing a null token")
@ -92,6 +93,7 @@ TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
#endif
}
#endif
#endif
bool IsImpersonating()
{
@ -186,6 +188,7 @@ TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
}
#endif // WIL_ENABLE_EXCEPTIONS
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
@ -251,6 +254,7 @@ TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLeve
RevertToSelf();
}
#endif
bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right)
{
@ -274,6 +278,7 @@ TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]")
REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS);
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]")
{
bool member;
@ -318,3 +323,4 @@ TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
}
#endif // WIL_ENABLE_EXCEPTIONS
#endif

View File

@ -0,0 +1,3 @@
#include <wil/Tracelogging.h>
// Just verify that Tracelogging.h compiles.

View File

@ -91,7 +91,7 @@ TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
int volatile countObserved = 0;
auto watcher = wil::make_event_watcher_nothrow([&]
{
countObserved++;
countObserved = countObserved + 1;
notificationReceived.SetEvent();
});
REQUIRE(watcher != nullptr);
@ -125,7 +125,7 @@ TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watch
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
{
allChangesMade.wait();
countObserved++;
countObserved = countObserved + 1;
lastObservedState = stateToObserve;
processedChange.SetEvent();
});
@ -213,7 +213,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]"
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
countObserved = countObserved + 1;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
@ -252,7 +252,7 @@ TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry
called = true;
allChangesMade.wait();
countObserved++;
countObserved = countObserved + 1;
lastObservedState = stateToObserve;
DWORD value, cbValue = sizeof(value);
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
@ -287,7 +287,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_wat
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
countObserved = countObserved + 1;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
@ -318,7 +318,7 @@ TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_wa
}
// Stress test, disabled by default
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]")
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[LocalOnly][registry][registry_watcher][stress]")
{
for (DWORD value = 0; value < 10000; ++value)
{
@ -355,12 +355,12 @@ TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_w
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
countObserved = countObserved + 1;
observedChangeType = changeType;
notificationReceived.SetEvent();
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
countObserved = countObserved + 1;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
@ -394,7 +394,7 @@ TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry]
notificationReceived.SetEvent();
// ensure that the callback is still being executed while the watcher is reset().
deleteNotification.wait(200);
deleteObserved++;
deleteObserved = deleteObserved + 1;
notificationReceived.SetEvent();
});

View File

@ -1,10 +1,10 @@
#include <time.h> // TODO: https://github.com/microsoft/wil/issues/44
#include <wil/winrt.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <map>
#include <string>
#include <vector>
#endif
// Required for pinterface template specializations that we depend on in this test
@ -240,8 +240,8 @@ void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
std::wstring lhsWstr(lhs);
std::wstring rhsWstr(rhs);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
@ -641,7 +641,7 @@ TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]")
REQUIRE(time1.UniversalTime == time2.UniversalTime);
}
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector(UINT32 count = 5)
{
auto result = Make<FakeVector<IInspectable*>>();
REQUIRE(result);
@ -649,7 +649,7 @@ ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
ComPtr<IPropertyValueStatics> propStatics;
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
for (UINT32 i = 0; i < 5; ++i)
for (UINT32 i = 0; i < count; ++i)
{
ComPtr<IInspectable> myProp;
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
@ -673,12 +673,12 @@ ComPtr<IVector<HSTRING>> MakeSampleStringVector()
return result;
}
ComPtr<IVector<Point>> MakeSamplePointVector()
ComPtr<IVector<Point>> MakeSamplePointVector(int count = 5)
{
auto result = Make<FakeVector<Point>>();
REQUIRE(result);
for (int i = 0; i < 5; ++i)
for (int i = 0; i < count; ++i)
{
auto value = static_cast<float>(i);
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
@ -687,6 +687,33 @@ ComPtr<IVector<Point>> MakeSamplePointVector()
return result;
}
template<typename T> auto cast_to(ComPtr<IInspectable> const& src)
{
ComPtr<IReference<T>> theRef;
T value{};
THROW_IF_FAILED(src.As(&theRef));
THROW_IF_FAILED(theRef->get_Value(&value));
return value;
}
TEST_CASE("WinRTTests::VectorToVectorTest", "[winrt][to_vector]")
{
#if defined(WIL_ENABLE_EXCEPTIONS)
auto uninit = wil::RoInitialize_failfast();
auto ints = MakeSampleInspectableVector(100);
auto vec = wil::to_vector(ints.Get());
UINT32 size;
THROW_IF_FAILED(ints->get_Size(&size));
REQUIRE(size == vec.size());
for (UINT32 i = 0; i < size; ++i)
{
ComPtr<IInspectable> oneItem;
THROW_IF_FAILED(ints->GetAt(i, &oneItem));
REQUIRE(cast_to<UINT32>(vec[i]) == cast_to<UINT32>(oneItem));
}
#endif
}
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
{
auto uninit = wil::RoInitialize_failfast();

View File

@ -0,0 +1,61 @@
#include <Windows.h>
#include <wincrypt.h>
#include <mscat.h>
#include <softpub.h>
#include <memory>
#include <wintrust.h>
#include <wil/resource.h>
#include "common.h"
#pragma comment(lib, "Wintrust.lib")
TEST_CASE("WilWintrustWrapperTest::VerifyWintrustDataAllocateAndFree", "[resource][wintrust]")
{
wil::unique_wintrust_data uwvtData;
uwvtData.cbStruct = sizeof(WINTRUST_DATA);
DWORD zero = 0;
REQUIRE(sizeof(WINTRUST_DATA) == uwvtData.cbStruct);
uwvtData.reset();
REQUIRE(zero == uwvtData.cbStruct);
}
TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATADMINAllocateAndFree", "[resource][wintrust]")
{
wil::unique_hcatadmin hCatAdmin;
REQUIRE(
CryptCATAdminAcquireContext2(
hCatAdmin.addressof(),
nullptr,
BCRYPT_SHA256_ALGORITHM,
nullptr,
0));
REQUIRE(hCatAdmin.get() != nullptr);
hCatAdmin.reset();
REQUIRE(hCatAdmin.get() == nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WilWintrustWrapperTest::VerifyUnqiueHCATINFOAllocate", "[resource][wintrust]")
{
wil::shared_hcatadmin hCatAdmin;
HCATINFO hCatInfo = nullptr;
REQUIRE(
CryptCATAdminAcquireContext2(
hCatAdmin.addressof(),
nullptr,
BCRYPT_SHA256_ALGORITHM,
nullptr,
0));
wil::unique_hcatinfo hCatInfoWrapper(hCatInfo, hCatAdmin);
REQUIRE(hCatInfoWrapper.get() == nullptr);
}
#endif

View File

@ -1,21 +1,13 @@
project(witest.app)
add_executable(witest.app)
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
target_compile_definitions(witest.app PRIVATE
-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP
)
target_sources(witest.app PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
target_sources(witest.app PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

File diff suppressed because it is too large Load Diff

View File

@ -204,6 +204,15 @@ namespace witest
#endif
}
[[noreturn]]
inline void __stdcall FakeFailfastWithContext(const wil::FailureInfo&) noexcept
{
::RaiseException(STATUS_STACK_BUFFER_OVERRUN, 0, 0, nullptr);
#ifdef __clang__
__builtin_unreachable();
#endif
}
constexpr DWORD msvc_exception_code = 0xE06D7363;
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
@ -241,6 +250,7 @@ namespace witest
{
// See above; we don't want to actually fail fast, so make sure we raise a different exception instead
auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException);
auto restoreHandler2 = AssignTemporaryValue(&wil::details::g_pfnFailfastWithContextCallback, FakeFailfastWithContext);
auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
@ -335,7 +345,7 @@ namespace witest
return S_OK;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
struct TestFolder
{

View File

@ -1,33 +1,28 @@
add_executable(witest.cpplatest)
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
# on compiler) as new standards are ratified/support is available
if (NOT MSVC)
set(CMAKE_CXX_STANDARD 17)
target_compile_features(witest.cpplatest PRIVATE cxx_std_20)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Clang is not compatible with the experimental coroutine header, so temporarily disable some headers until full
# C++20 support is available
set(COROUTINE_SOURCES)
else()
set(COROUTINE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/../ComApartmentVariableTests.cpp)
endif()
project(witest.cpplatest)
add_executable(witest.cpplatest)
# Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0"
if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0")
target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp)
endif()
target_sources(witest.cpplatest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
target_sources(witest.cpplatest PRIVATE
${COMMON_SOURCES}
${COROUTINE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

View File

@ -6,3 +6,15 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#if WITEST_ADDRESS_SANITIZER
extern "C" __declspec(dllexport)
const char* __asan_default_options()
{
return
// Tests validate OOM, so this is expected
"allocator_may_return_null=1"
// Some structs in Windows have dynamic size where we over-allocate for extra data past the end
":new_delete_type_mismatch=0";
}
#endif

View File

@ -1,27 +1,23 @@
project(witest.noexcept)
add_executable(witest.noexcept)
# Turn off exceptions for this test
replace_cxx_flag("/EHsc" "")
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
replace_cxx_flag("/EHsc" "/EHs-c-")
target_compile_definitions(witest.noexcept PRIVATE
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
)
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
# statements... Thankfully MSVC just gives us a warning that we can disable
append_cxx_flag("/wd4530")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(witest.noexcept PRIVATE /wd4530)
endif()
target_sources(witest.noexcept PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
target_sources(witest.noexcept PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

View File

@ -1,21 +1,12 @@
project(witest)
add_executable(witest)
target_sources(witest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
target_sources(witest PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

View File

@ -0,0 +1,49 @@
add_executable(witest.asan)
target_compile_options(witest.asan PRIVATE -fsanitize=address)
target_compile_definitions(witest.asan PRIVATE
-DCATCH_CONFIG_NO_WINDOWS_SEH # ASAN relies on first chance AVs
-DWITEST_ADDRESS_SANITIZER # To conditionally enable/disable code
)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_definitions(witest.asan PRIVATE
# Not compatible with using lld-link
-D_DISABLE_VECTOR_ANNOTATION
# See below; not compatible with exceptions
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
)
# Clang ASan on Windows has issues with exceptions: https://github.com/google/sanitizers/issues/749
replace_cxx_flag("/EHsc" "/EHs-c-")
if ($ENV{Platform} STREQUAL "x86")
target_link_libraries(witest.asan PRIVATE
clang_rt.asan_dynamic-i386.lib
clang_rt.asan_dynamic_runtime_thunk-i386.lib
)
else()
target_link_libraries(witest.asan PRIVATE
clang_rt.asan_dynamic-x86_64.lib
clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
)
endif()
else()
# Using exceptions, so we can compile the STL tests
set(EXTRA_SOURCES
${EXTRA_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
)
endif()
target_sources(witest.asan PUBLIC
${COMMON_SOURCES}
${EXTRA_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

View File

@ -0,0 +1,25 @@
add_executable(witest.ubsan)
target_compile_options(witest.ubsan PRIVATE
-fsanitize=undefined
-fno-sanitize-recover=undefined # So we get test failures
)
target_compile_definitions(witest.ubsan PRIVATE
-DWITEST_UB_SANITIZER # To conditionally enable/disable code
)
# UBSan libraries were built assuming static linking
set_property(TARGET witest.ubsan
PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
target_sources(witest.ubsan PUBLIC
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

View File

@ -3,12 +3,15 @@
#include <wil/resource.h>
#include <wil/win32_helpers.h>
#include <wil/filesystem.h>
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
#include <wil/wrl.h>
#endif
#include <wil/com.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <memory>
#include <set>
#include <thread>
#include <unordered_set>
#endif
@ -96,7 +99,7 @@ void TestErrorCallbacks()
return false;
});
size_t const depthCount = 10;
constexpr size_t depthCount = 10;
for (size_t index = 0; index < depthCount; index++)
{
LOG_HR(E_ACCESSDENIED);
@ -146,7 +149,7 @@ void StressErrorCallbacks()
{
auto restore = witest::AssignTemporaryValue(&wil::g_fResultOutputDebugString, false);
size_t const threadCount = 20;
constexpr size_t threadCount = 20;
wil::unique_event eventArray[threadCount];
for (size_t index = 0; index < threadCount; index++)
@ -164,7 +167,7 @@ void StressErrorCallbacks()
}
}
TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[!hide][result_macros][stress]")
TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[LocalOnly][result_macros][stress]")
{
auto restore = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, EmptyResultMacrosLoggingCallback);
StressErrorCallbacks();
@ -485,8 +488,8 @@ TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]")
REQUIRE_RETURNS_EXPECTED(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_ASSERTION_FAILURE); return S_OK; });
REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE)); });
REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__)); });
REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE))); });
REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__))); });
REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
@ -495,8 +498,8 @@ TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]")
REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_NO_MEMORY); return S_OK; });
REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY)); });
REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__)); });
REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY))); });
REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__))); });
REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
@ -1015,9 +1018,13 @@ TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]")
wchar_t tempFileName[MAX_PATH];
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName));
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(params) };
params.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
wil::unique_hfile spValidHandle(::CreateFile2(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, &params));
#else
wil::unique_hfile spValidHandle(::CreateFileW(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr));
#endif
::DeleteFileW(tempFileName);
REQUIRE(spValidHandle.get() != INVALID_HANDLE_VALUE);
@ -1072,9 +1079,13 @@ TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]")
wchar_t tempFileName2[MAX_PATH];
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName2));
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(params2) };
params2.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
*(&spMoveHandle) = ::CreateFile2(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, &params2);
#else
*(&spMoveHandle) = ::CreateFileW(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
#endif
::DeleteFileW(tempFileName2);
REQUIRE(spMoveHandle);
@ -1434,6 +1445,62 @@ void SemaphoreTestCommon()
REQUIRE(eManual.try_open(L"BAR-TEST"));
}
template <typename test_t>
void MutexRaiiTests()
{
test_t var1;
var1.create();
{
REQUIRE(var1.acquire());
}
// try_create
bool exists = false;
REQUIRE(var1.try_create(L"wiltestmutex", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE_FALSE(exists);
test_t var2;
REQUIRE(var2.try_create(L"wiltestmutex", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE(exists);
test_t var3;
REQUIRE_FALSE(var3.try_create(L"\\illegal\\chars\\too\\\\many\\\\namespaces", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE(::GetLastError() != ERROR_SUCCESS);
// try_open
test_t var4;
REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces"));
REQUIRE(::GetLastError() != ERROR_SUCCESS);
REQUIRE(var4.try_open(L"wiltestmutex"));
}
template <typename test_t>
void SemaphoreRaiiTests()
{
test_t var1;
var1.create(1, 1);
{
REQUIRE(var1.acquire());
}
// try_create
bool exists = false;
REQUIRE(var1.try_create(1, 1, L"wiltestsemaphore", MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE_FALSE(exists);
test_t var2;
REQUIRE(var2.try_create(1, 1, L"wiltestsemaphore", MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE(exists);
test_t var3;
REQUIRE_FALSE(var3.try_create(1, 1, L"\\illegal\\chars\\too\\\\many\\\\namespaces", MUTEX_ALL_ACCESS, nullptr, &exists));
REQUIRE(::GetLastError() != ERROR_SUCCESS);
// try_open
test_t var4;
REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces"));
REQUIRE(::GetLastError() != ERROR_SUCCESS);
REQUIRE(var4.try_open(L"wiltestsemaphore"));
}
TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
{
EventTestCommon<wil::unique_event_nothrow>();
@ -1467,15 +1534,17 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
wil::unique_event_nothrow testEventNoExcept;
REQUIRE(SUCCEEDED(testEventNoExcept.create(wil::EventOptions::ManualReset)));
MutexTestCommon<wil::unique_mutex_nothrow>();
MutexTestCommon<wil::unique_mutex_failfast>();
MutexRaiiTests<wil::unique_mutex_nothrow>();
MutexRaiiTests<wil::unique_mutex_failfast>();
// intentionally disabled in the non-exception version...
// wil::unique_mutex_nothrow testMutex2(L"FOO-TEST-2");
wil::unique_mutex_failfast testMutex3(L"FOO-TEST-3");
#ifdef WIL_ENABLE_EXCEPTIONS
MutexTestCommon<wil::unique_mutex>();
MutexRaiiTests<wil::unique_mutex>();
wil::unique_mutex testMutex(L"FOO-TEST");
WaitForSingleObjectEx(testMutex.get(), INFINITE, TRUE);
@ -1494,12 +1563,15 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
SemaphoreTestCommon<wil::unique_semaphore_nothrow>();
SemaphoreTestCommon<wil::unique_semaphore_failfast>();
SemaphoreRaiiTests<wil::unique_semaphore_nothrow>();
SemaphoreRaiiTests<wil::unique_semaphore_failfast>();
// intentionally disabled in the non-exception version...
// wil::unique_semaphore_nothrow testSemaphore2(1, 1);
wil::unique_semaphore_failfast testSemaphore3(1, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
SemaphoreTestCommon<wil::unique_semaphore>();
SemaphoreRaiiTests<wil::unique_semaphore>();
wil::unique_semaphore testSemaphore(1, 1);
WaitForSingleObjectEx(testSemaphore.get(), INFINITE, true);
@ -1683,6 +1755,15 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
auto unique_bstr_nothrow2 = wil::make_bstr_nothrow(L"");
REQUIRE(wcscmp(L"", unique_bstr_nothrow2.get()) == 0);
auto unique_variant_bstr_failfast1 = wil::make_variant_bstr_failfast(L"Foo");
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_failfast1.addressof(), bstrVal)) == 0);
auto unique_variant_bstr_nothrow1 = wil::make_variant_bstr_nothrow(L"Foo");
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_nothrow1.addressof(), bstrVal)) == 0);
auto unique_variant_bstr_nothrow2 = wil::make_variant_bstr_nothrow(L"");
REQUIRE(wcscmp(L"", V_UNION(unique_variant_bstr_nothrow2.addressof(), bstrVal)) == 0);
#ifdef WIL_ENABLE_EXCEPTIONS
auto unique_bstr_te1 = wil::make_bstr(L"Foo");
REQUIRE(wcscmp(L"Foo", unique_bstr_te1.get()) == 0);
@ -1690,6 +1771,11 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
auto unique_bstr_te2 = wil::make_bstr(L"");
REQUIRE(wcscmp(L"", unique_bstr_te2.get()) == 0);
auto unique_variant_bstr_te1 = wil::make_variant_bstr(L"Foo");
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_te1.addressof(), bstrVal)) == 0);
auto unique_variant_bstr_te2 = wil::make_variant_bstr(L"");
REQUIRE(wcscmp(L"", V_UNION(unique_variant_bstr_te2.addressof(), bstrVal)) == 0);
auto testString = wil::make_cotaskmem_string(L"Foo");
{
@ -1774,10 +1860,13 @@ TEST_CASE("WindowsInternalTests::Locking", "[resource]")
{
CRITICAL_SECTION cs;
::InitializeCriticalSectionEx(&cs, 0, 0);
auto lock = wil::EnterCriticalSection(&cs);
REQUIRE(lock);
auto tryLock = wil::TryEnterCriticalSection(&cs);
REQUIRE(tryLock);
{
auto lock = wil::EnterCriticalSection(&cs);
REQUIRE(lock);
auto tryLock = wil::TryEnterCriticalSection(&cs);
REQUIRE(tryLock);
}
::DeleteCriticalSection(&cs);
}
{
wil::critical_section cs;
@ -2451,6 +2540,43 @@ TEST_CASE("WindowsInternalTests::Win32HelperTests", "[win32_helpers]")
REQUIRE(systemTimePlusOneHour64 == (systemTime64 + wil::filetime_duration::one_hour));
}
TEST_CASE("WindowsInternalTests::RectHelperTests", "[win32_helpers]")
{
RECT rect{ 50, 100, 200, 300 };
POINT leftEdgePoint{ 50, 150 };
POINT topEdgePoint{ 100, 100 };
POINT rightEdgePoint{ 200, 150 };
POINT bottomEdgePoint{ 100, 300 };
POINT insidePoint{ 150, 150};
RECT emptyRectAtOrigin{};
RECT emptyRectNotAtOrigin{ 50, 50, 50, 50 };
RECT nonNormalizedRect{ 300, 300, 0, 0 };
REQUIRE(wil::rect_width(rect) == 150);
REQUIRE(wil::rect_height(rect) == 200);
// rect_is_empty should work like user32's IsRectEmpty
REQUIRE_FALSE(wil::rect_is_empty(rect));
REQUIRE(wil::rect_is_empty(emptyRectAtOrigin));
REQUIRE(wil::rect_is_empty(emptyRectNotAtOrigin));
REQUIRE(wil::rect_is_empty(nonNormalizedRect));
// rect_contains_point should work like user32's PtInRect
REQUIRE(wil::rect_contains_point(rect, insidePoint));
REQUIRE(wil::rect_contains_point(rect, leftEdgePoint));
REQUIRE(wil::rect_contains_point(rect, topEdgePoint));
REQUIRE_FALSE(wil::rect_contains_point(rect, rightEdgePoint));
REQUIRE_FALSE(wil::rect_contains_point(rect, bottomEdgePoint));
REQUIRE_FALSE(wil::rect_contains_point(nonNormalizedRect, insidePoint));
auto rectFromSize = wil::rect_from_size<RECT>(50, 100, 150, 200);
REQUIRE(rectFromSize.left == rect.left);
REQUIRE(rectFromSize.top == rect.top);
REQUIRE(rectFromSize.right == rect.right);
REQUIRE(rectFromSize.bottom == rect.bottom);
}
TEST_CASE("WindowsInternalTests::InitOnceNonTests")
{
bool called = false;
@ -2494,7 +2620,8 @@ TEST_CASE("WindowsInternalTests::InitOnceNonTests")
init = {};
// A thrown exception leaves the object un-initialized
REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; throw wil::ResultException(E_FAIL); }), wil::ResultException);
static volatile bool always_true = true; // So that the compiler can't determine that we unconditionally throw below (warning C4702)
REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; THROW_HR_IF(E_FAIL, always_true); }), wil::ResultException);
REQUIRE_FALSE(wil::init_once_initialized(init));
REQUIRE(called);
REQUIRE_FALSE(winner);
@ -2930,7 +3057,7 @@ TEST_CASE("WindowsInternalTests::TestUniqueArrayCases", "[resource]")
}
#endif
#ifndef __cplusplus_winrt
#if !defined(__cplusplus_winrt) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
TEST_CASE("WindowsInternalTests::VerifyMakeAgileCallback", "[wrl]")
{
using namespace ABI::Windows::Foundation;
@ -2987,6 +3114,7 @@ TEST_CASE("WindowsInternalTests::Ranges", "[common]")
{
++count;
m = 1;
(void)m;
}
REQUIRE(ARRAYSIZE(things) == count);
REQUIRE(0 == things[0]);
@ -3084,12 +3212,12 @@ void ThreadPoolWaitTestHelper(bool requireExactCallbackCount)
REQUIRE_SUCCEEDED(myContext.Event.create());
WaitResourceT wait;
wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, NULL));
wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, nullptr));
REQUIRE(wait);
SetThreadpoolWait(wait.get(), myContext.Event.get(), nullptr);
const int loopCount = 5;
constexpr int loopCount = 5;
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
{
// Signal event.
@ -3149,10 +3277,10 @@ void ThreadPoolWaitWorkHelper(bool requireExactCallbackCount)
ThreadPoolWaitWorkContext myContext;
WaitResourceT work;
work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, NULL));
work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, nullptr));
REQUIRE(work);
const int loopCount = 5;
constexpr int loopCount = 5;
for (int itr = 0; itr != loopCount; ++itr)
{
SubmitThreadpoolWork(work.get());
@ -3202,7 +3330,7 @@ void ThreadPoolTimerWorkHelper(SetThreadpoolTimerT const &setThreadpoolTimerFn,
timer.reset(CreateThreadpoolTimer(ThreadPoolTimerWorkCallback, &myContext, nullptr));
REQUIRE(timer);
const int loopCount = 5;
constexpr int loopCount = 5;
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
{
// Schedule timer
@ -3256,7 +3384,7 @@ TEST_CASE("WindowsInternalTests::ThreadPoolTimerTest", "[resource][unique_thread
ThreadPoolTimerWorkHelper<wil::unique_threadpool_timer_nocancel, FILETIME>(SetThreadpoolTimer, true);
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
static void __stdcall SlimEventTrollCallback(
_Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
_Inout_opt_ void* context,
@ -3328,7 +3456,7 @@ TEST_CASE("WindowsInternalTests::SlimEventTests", "[resource][slim_event]")
}
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
struct ConditionVariableCSCallbackContext
{
@ -3469,4 +3597,88 @@ TEST_CASE("WindowsInternalTests::ShutdownAwareObjectAlignmentTests", "[result_ma
VerifyAlignment<wil::object_without_destructor_on_shutdown>();
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
TEST_CASE("WindowsInternalTests::ModuleReference", "[wrl]")
{
REQUIRE(::Microsoft::WRL::GetModuleBase() == nullptr);
// Executables don't have a ModuleBase, so we need to create one.
struct FakeModuleBase : Microsoft::WRL::Details::ModuleBase
{
unsigned long count = 42;
STDMETHOD_(unsigned long, IncrementObjectCount)()
{
return InterlockedIncrement(&count);
}
STDMETHOD_(unsigned long, DecrementObjectCount)()
{
return InterlockedDecrement(&count);
}
STDMETHOD_(unsigned long, GetObjectCount)() const
{
return count;
}
// Dummy implementations of everything else (never called).
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetFirstEntryPointer)() const { return nullptr; }
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetMidEntryPointer)() const { return nullptr; }
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetLastEntryPointer)() const { return nullptr; }
STDMETHOD_(SRWLOCK*, GetLock)() const { return nullptr; }
STDMETHOD(RegisterWinRTObject)(const wchar_t*, const wchar_t**, _Inout_ RO_REGISTRATION_COOKIE*, unsigned int) { return E_NOTIMPL; }
STDMETHOD(UnregisterWinRTObject)(const wchar_t*, _In_ RO_REGISTRATION_COOKIE) { return E_NOTIMPL; }
STDMETHOD(RegisterCOMObject)(const wchar_t*, _In_ IID*, _In_ IClassFactory**, _Inout_ DWORD*, unsigned int) { return E_NOTIMPL; }
STDMETHOD(UnregisterCOMObject)(const wchar_t*, _Inout_ DWORD*, unsigned int) { return E_NOTIMPL; }
};
FakeModuleBase fake;
auto peek_module_ref_count = []()
{
return ::Microsoft::WRL::GetModuleBase()->GetObjectCount();
};
auto initial = peek_module_ref_count();
// Basic test: Construct and destruct.
{
auto module_ref = wil::wrl_module_reference();
REQUIRE(peek_module_ref_count() == initial + 1);
}
REQUIRE(peek_module_ref_count() == initial);
// Fancy test: Copy object with embedded reference.
{
struct object_with_ref
{
wil::wrl_module_reference ref;
};
object_with_ref o1;
REQUIRE(peek_module_ref_count() == initial + 1);
auto o2 = o1;
REQUIRE(peek_module_ref_count() == initial + 2);
o1 = o2;
REQUIRE(peek_module_ref_count() == initial + 2);
o2 = std::move(o1);
REQUIRE(peek_module_ref_count() == initial + 2);
}
REQUIRE(peek_module_ref_count() == initial);
}
#endif
#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(NTDDI_WIN10_CO) ? \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES))
TEST_CASE("WindowsInternalTests::VerifyModuleReferencesForThread", "[win32_helpers]")
{
bool success = true;
std::thread([&]
{
auto moduleRef = wil::get_module_reference_for_thread();
moduleRef.reset(); // results in exiting the thread
// should never get here
success = false;
FAIL();
}).join();
REQUIRE(success);
}
#endif
#pragma warning(pop)

13
Externals/WIL/tests/win7/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,13 @@
add_executable(witest.win7)
target_compile_definitions(witest.win7 PRIVATE
-D_WIN32_WINNT=0x0601
)
target_sources(witest.win7 PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
)

View File

@ -12,14 +12,6 @@
#pragma once
#endif // _MSC_VER
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpragma-pack"
#pragma clang diagnostic ignored "-Wunused-value"
#pragma clang diagnostic ignored "-Wmicrosoft-sealed"
#pragma clang diagnostic ignored "-Winaccessible-base"
#endif
#pragma region includes
#include <inspectable.h>
@ -599,6 +591,8 @@ struct VerifyInheritanceHelper<I, Nil>
} // namespace Details
// note: Due to potential shutdown ordering issues, the results of GetModuleBase
// should always be checked for null on reference counting and cleanup operations.
inline Details::ModuleBase* GetModuleBase() throw()
{
return Details::ModuleBase::module_;
@ -840,7 +834,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
I0,
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -890,7 +884,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
Selector<I0, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>,
Selector<typename AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -935,7 +929,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type,
AdjustImplements<RuntimeClassFlagsT, true, I1, TInterfaces...>::Type
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
protected:
@ -976,7 +970,7 @@ template <typename RuntimeClassFlagsT, bool doStrictCheck, typename I0>
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, CloakedIid<I0>> :
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
protected:
@ -1012,7 +1006,7 @@ protected:
template <typename RuntimeClassFlagsT, bool doStrictCheck>
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck>
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -1041,7 +1035,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
ChainInterfaces<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>,
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -1086,7 +1080,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
{
static_assert(hasImplements, "Cannot use MixIn to with a class not deriving from \"Implements\"");
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -1148,7 +1142,7 @@ template <typename RuntimeClassFlagsT, typename FactoryInterface, bool doStrictC
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>, TInterfaces...> :
ImplementsHelper<RuntimeClassFlagsT, true, ComposableBase<FactoryInterface>>
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -1184,7 +1178,7 @@ protected:
template <typename RuntimeClassFlagsT, typename FactoryInterface, bool doStrictCheck>
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>>
{
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
protected:
@ -1282,7 +1276,7 @@ public:
typedef I0 FirstInterface;
protected:
typedef typename Details::AdjustImplements<RuntimeClassFlags<WinRt>, true, I0, TInterfaces...>::Type BaseType;
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct Details::ImplementsHelper;
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TOtherInterfaces> friend struct Details::ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
@ -1317,7 +1311,7 @@ public:
protected:
typedef typename Details::AdjustImplements<RuntimeClassFlags<flags>, true, I0, TInterfaces...>::Type BaseType;
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct Details::ImplementsHelper;
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TOtherInterfaces> friend struct Details::ImplementsHelper;
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
@ -1523,6 +1517,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchange
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchange
#elif defined(_ARM_)
@ -1532,6 +1528,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
#elif defined(_ARM64_)
@ -1541,6 +1539,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
#else
@ -1562,6 +1562,37 @@ class __declspec(novtable) RuntimeClassImpl;
// PREFast cannot see through template instantiation for AsIID()
#pragma warning(disable: 6388)
// Reference counting functions that check overflow. If overflow is detected, ref count value will stop at LONG_MAX, and the object being
// reference-counted will be leaked.
inline unsigned long SafeUnknownIncrementReference(long volatile &refcount) throw()
{
long oldValue = refcount;
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForIncrement(&refcount, oldValue + 1, oldValue) != oldValue))
{
oldValue = refcount;
}
if (oldValue != LONG_MAX)
{
return oldValue + 1;
}
else
{
return LONG_MAX;
}
}
inline unsigned long SafeUnknownDecrementReference(long volatile &refcount) throw()
{
long oldValue = refcount;
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForRelease(&refcount, oldValue - 1, oldValue) != oldValue))
{
oldValue = refcount;
}
return oldValue - 1;
}
template <class RuntimeClassFlagsT, bool implementsWeakReferenceSource, bool implementsFtmBase, typename ...TInterfaces>
class __declspec(novtable) RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, false, implementsFtmBase, TInterfaces...> :
public Details::AdjustImplements<RuntimeClassFlagsT, false, TInterfaces...>::Type,
@ -1624,7 +1655,7 @@ protected:
#ifdef _PERF_COUNTERS
IncrementAddRefCount();
#endif
return UnknownIncrementReference(&refcount_);
return SafeUnknownIncrementReference(refcount_);
}
unsigned long InternalRelease() throw()
@ -1634,7 +1665,7 @@ protected:
#endif
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&refcount_);
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -1786,7 +1817,7 @@ protected:
#ifdef _PERF_COUNTERS
IncrementAddRefCount();
#endif
return UnknownIncrementReference(&refcount_);
return SafeUnknownIncrementReference(refcount_);
}
unsigned long InternalRelease() throw()
@ -1796,7 +1827,7 @@ protected:
#endif
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&refcount_);
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -1828,14 +1859,14 @@ public:
unsigned long IncrementStrongReference() throw()
{
return UnknownIncrementReference(&strongRefCount_);
return SafeUnknownIncrementReference(strongRefCount_);
}
unsigned long DecrementStrongReference() throw()
{
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&strongRefCount_);
unsigned long newValue = SafeUnknownDecrementReference(strongRefCount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -2089,7 +2120,7 @@ inline INT_PTR EncodeWeakReferencePointer(Microsoft::WRL::Details::WeakReference
inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value)
{
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(value << 1);
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(static_cast<UINT_PTR>(value) << 1);
}
#pragma warning(pop) // C6388
@ -2134,6 +2165,7 @@ class RuntimeClass<InterfaceListHelper<TInterfaces...>, RuntimeClassFlagsT, impl
public RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, implementsInspectable, implementsFtmBase, TInterfaces...>
{
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2153,6 +2185,7 @@ class RuntimeClass :
RuntimeClass(const RuntimeClass&);
RuntimeClass& operator=(const RuntimeClass&);
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2177,6 +2210,7 @@ class RuntimeClass<RuntimeClassFlags<classFlags>, TInterfaces...> :
RuntimeClass(const RuntimeClass&);
RuntimeClass& operator=(const RuntimeClass&);
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2196,8 +2230,8 @@ public:
namespace Details
{
//Weak reference implementation
class WeakReferenceImpl sealed:
// Weak reference implementation
class WeakReferenceImpl final :
public ::Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, IWeakReference>,
public StrongReference
{
@ -2289,6 +2323,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
{
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
{
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
{
return LONG_MAX;
}
UINT_PTR updateValue = currentValue.refCount + 1;
#ifdef __WRL_UNITTEST__
@ -2324,6 +2363,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
{
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
{
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
{
return LONG_MAX - 1;
}
UINT_PTR updateValue = currentValue.refCount - 1;
#ifdef __WRL_UNITTEST__
@ -2432,8 +2476,13 @@ public:
// Allocate memory with operator new(size, nothrow) only
// This will allow developer to override one operator only
// to enable different memory allocation model
buffer_ = (char*) (operator new (sizeof(T), std::nothrow));
return buffer_;
#ifdef __cpp_aligned_new
if constexpr (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
return buffer_ = (char*) operator new (sizeof(T), static_cast<std::align_val_t>(alignof(T)), ::std::nothrow);
}
#endif // /std:c++17 or later
return buffer_ = (char*) operator new (sizeof(T), ::std::nothrow);
}
void Detach() throw()
@ -2526,7 +2575,7 @@ namespace Details
{ \
return trustLevel; \
} \
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) \
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) override \
{ \
*runtimeName = nullptr; \
HRESULT hr = S_OK; \
@ -2537,7 +2586,7 @@ namespace Details
} \
return hr; \
} \
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) \
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) override \
{ \
*trustLvl = trustLevel; \
return S_OK; \
@ -2545,22 +2594,22 @@ namespace Details
STDMETHOD(GetIids)(_Out_ ULONG *iidCount, \
_When_(*iidCount == 0, _At_(*iids, _Post_null_)) \
_When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) \
_Result_nullonfailure_ IID **iids) \
_Result_nullonfailure_ IID **iids) override \
{ \
return RuntimeClassT::GetIids(iidCount, iids); \
} \
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) \
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) override \
{ \
bool handled = false; \
HRESULT hr = this->CustomQueryInterface(riid, ppvObject, &handled); \
if (FAILED(hr) || handled) return hr; \
return RuntimeClassT::QueryInterface(riid, ppvObject); \
} \
STDMETHOD_(ULONG, Release)() \
STDMETHOD_(ULONG, Release)() override \
{ \
return RuntimeClassT::Release(); \
} \
STDMETHOD_(ULONG, AddRef)() \
STDMETHOD_(ULONG, AddRef)() override \
{ \
return RuntimeClassT::AddRef(); \
} \
@ -2648,8 +2697,4 @@ namespace Details
// Restore packing
#include <poppack.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // _WRL_IMPLEMENTS_H_